AdonisJs and Koa style middleware layer for general use.
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
bin
src
test
.editorconfig
.gitignore
.travis.yml
CHANGELOG.md
README.md
appveyor.yml
index.js
package-lock.json
package.json

README.md

Co Compose

Compose an array of functions to be executed one after the other. Similar to Koa and AdonisJs.

NPM Version Build Status Downloads Stats Appveyor

Co compose composes an array of middleware to be executed in sequence. The library is framework independent and can be used in any Javascript project.

Pattern

It follows the middleware pattern with following traits.

  1. Each method is called in sequence after next is called.
  2. If next is not called, the middleware chain will short-circuit and resolves right away.
  3. Any middleware function can break the chain by throwing an exception.
  4. All middleware functions after next call are executed in reverse order.
const middleware = new Middleware()

const logs = []

async function first (next) {
  logs.push('first')
  await next()
  logs.push('first: in reverse')
}

async function second (next) {
  logs.push('second')
  await next()
  logs.push('second: in reverse')
}

async function third (next) {
  logs.push('third')
  await next()
  logs.push('third: in reverse')
}

middleware.register([first, second, third])

await middleware.runner().run()
assert.deepEqual(logs, [
  'first',
  'second',
  'third',
  'third: in reverse',
  'second: in reverse',
  'first: in reverse'
])

Usage

Start by importing the library and instantiating a new instance of it.

const Middleware = require('co-compose')
const middleware = new Middleware()

Next, register the middleware functions.

middleware.register([
  async function (next) {
    await next()
  },
  async function (next) {
    await next()
  }
])

Pull an instance of middleware runner to executed the registered middleware.

const runner = middleware.runner()

runner
  .run()
  .then(() => {
    console.log('middleware executed')
  })
  .catch((error) => {
    console.log(error)
  })

Passing data along

A common use case of middleware is the HTTP request lifecycle. Let's see how we to pass the req and res objects to the middleware functions.

const http = require('http')
const middleware = new (require('co-compose'))()

middleware.register([
  async function (req, res, next) {
    req.greeting = 'Hello world'
    await next()
  },

  async function (req, res, next) {
    res.write(req.greeting)
    await next()
  }
])

http.createServer(async function (req, res) {
  const runner = middleware.runner()

  // passing data
  runner.params([req, res])

  runner
    .run()
    .then(() => {
      res.end()
    })
    .catch((error) => {
      res.end(error.message)
    })
}).listen(3000)

The params method accepts an array of values as pass them as arguments to the middleware.

Middleware API

register([fns])

An array of functions to be executed as middleware. Calling this method for multiple times, will concat to the existing list.

middleware.register([fn1, fn2])

runner()

Returns an instance of runner with the registered middleware.

middleware
  .runner()
  .run()
  .then(console.log)
  .catch(console.error)

Runner API

params([values])

An array of values to be passed to the middleware functions as arguments. Each value inside array is passed as a seperate argument.

const runner = middleware.runner()
runner.params([req, res])

run() -> Promise

Execute the middleware chain

const runner = middleware.runner()

runner
  .run()
  .then(console.log)
  .catch(console.error)

concat([fns])

Concat middleware functions just before executing them. This method is useful when middleware list is known at runtime.

const runner = middleware.runner()
runner.concat([fn3, fn4])