Skip to content

emilniklas/ellie

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ellie

Code Style: StandardJS Build Status

Usage

Simple installation

> echo {} > package.json
> npm install --save ellie eliot
> touch index.js

Simple API

// index.js

import { serve } from 'ellie'

const HelloWorld = () => (request) => (
  <div>
    <h1>Hello World</h1>
    <pre>{request.method} {request.url}</pre>
  </div>
)

serve(HelloWorld)
  .listen(8080)
  .then(() => console.log('Listening to port 8080'))

Run instantly

> eliot index.js --jsx
Listening to port 8080

In depth

Ellie uses a promise based middleware system, inspired by Dart's Shelf package. All the data structures are implemented in an immutable way, so that you have absolute control over the requests and responses in your application.

A "pipeline" is constructed by a list of middleware. A middleware is a function or a class that receives a reference to the next middleware, and handles an incoming request. The middleware should always either return a response or throw an error. It can either create the response itself (i.e. return a value that either is a Response, or can be casted to a Response) or pass the request into the next middleware and get a response back.

It can also create a new pipeline and pass the request into that one instead.

You can think of it as a tree. A request is passed in through the trunk, and each branch returns a response or throws an error. Each junction or split in a branch is dictated by middleware.

The implementation of a middleware can be broken down into steps. The middleware receives the next middleware and returns the function that receives the request.

(next) => (request) => 'response'

Expanded, it could look something like this:

const MyMiddleware = (next) => {
  // Any set up can be done here. Will run once.

  return async function (request) {
    // This is run for every request, obviously. Must return a response.

    // Option 1. Delegate to the next middleware
    return next(request)

    // Option 2. Return response
    return 'Hello world!'

    // Option 3. Throw an error
    throw new Error('Something went wrong!')

    // Option 4. Delegate to other pipeline
    return someOtherPipeline.pipe(request)
  }
}

// Used like this
pipe(
  MyMiddleware
)

If your middleware needs arguments, you can simply wrap it in another function.

const MyMiddleware = (argument) => (next) => (request) => ...

// Used like this
pipe(
  MyMiddleware('some argument')
)

The pipeline is made from multiple middleware, like this:

pipe(
  (next) => async function (request) {
    // Do something with request
    const response = await next(request)
    // Do something with response
    return response
  },
  (next) => async function (request) {
    // Do something with request
    const response = await next(request)
    // Do something with response
    return response
  },
  () => async function (request) {
    // Do something with request
    return 'some response'
  }
)

About

Next gen server framework

Resources

License

Stars

Watchers

Forks

Packages

No packages published