Express.js and the Chain of Responsability design pattern

Express is a small but powerful web framework. In this article we will explore how Expressjs uses a behavioral design pattern to leverage its superpowers.

Express web framework

Express is a minimalist web framework for Node. It does not force you to use any ORM or template engine despite of supporting many of them.

Express philosophy is to be small but “_craftable_”. It accomplishes this by providing a robust routing system and middlewares to decouple the request from its receivers.

Divide and conquer: Routers and Middlewares

Express decouples the sender from its receivers using middlewares. Each middleware can act on the request (if needed) and/or pass it to the next receiver in the chain.

While Express uses the term middleware, if we roll-back and re-read the chain of Responsability behavioral pattern, we will see that this middleware is an implementation of the chain of responsability pattern.

Example

const express = require('express')
const app = express()

// 1st middleware/receiver
// Validates request and pass to next receiver
const validateMiddleware = (req, res, next) => {
  if (isValid()){
    next()  
  } else {
    next(new Error('Wrong params'))
  }
}

// 2nd middleware/receiver
// Find post
const postFinderMiddleware = (req, res, next) {
  PostService.get(req.params.id)
    .then( post => {
      if(post !== null) {
        res.send(post)  
      } else {
        res.status(404).send([])
      }

    })
    .catch( e => res.send(e) )
}

// 3rd middleware/receiver
// Handling errors
const errorHandlingMiddleware = (err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
}


// Request will be passed through the receiver's chain
app.get('/post/:id'
  , validateMiddleware
  , postFinderMiddleware
  , errorHandlingMiddleware
)

Revelaing Express superpowers for your own usage

The Chain of Responsability is a behavioral design pattern contained in the GoF design patterns book. It avoids coupling the sender of a request to its receivers. The pattern creates a chain of receivers to process the request. The order in which receivers are placed in the chain determines the receiver’s processing order. Once the processing chain start, receivers can decide to handle the request and/or continue and pass the request it to the next receiver.

Also if the request can be processed in different ways, the chain of responsability pattern can be applied to avoid if-elseif-elseif-elseif-else hell. This design pattern blendes itself quite well for splitting up the handling of a request in small tasks.

class AbstractHandler {

  constructor(){
    this._nextHandler = null
  }

  nextHandler(handler){
    this._nextHandler = handler
  }

  process(data){
    throw new Error('AbstractMethod: Missing implementation')
  }
}

/*
 * Specialized handler 
 */
class HandlerHeader extends AbstractHandler {
  process(data) {
    if (data.header['x-token']) {
      /**/
    }

    if (this._nextHandler !== null){
      this._nextHandler.process(data)  
    }
  }
}

class HandlerBody extends AbstractHandler {
  process(data) {
    if (data.body) {
      console.log('Handling body request')
    }

    if (this._nextHandler !== null){
      this._nextHandler.process(data)
    }
  }
}

Usage:

const headerHandler = new HandlerHeader()
const bodyHandler = new HandlerBody()

headerHandler.nextHandler(bodyHandler)
headerHandler.process({header: { type: 'json' }, body: { content: 'Hello World' }})