6 min read

This post provides you with an introduction to Express middleware functions, what they are, and how you can apply a composability principle to combine simple middleware functions to build more complicated ones. You can find the code of this article at github.com/revington/express-middleware-tutorial.

Before digging into Express and its middleware functions, let’s examine the concept of a web server in Node. In Node, we do not create web applications as in PHP or ASP, but with web servers.

Hello world Node web server

Node web servers are created by exposing an HTTP handler to an instance of the HTTP class. This handler will have access to the request object, an instance of IncomingMessage; and the response object, an instance of ServerResponse.

The following code listing (hello.js) implements this concept:

var http = require('http');

function handler(request, response){
    response.end('hello world');
}

http.createServer(handler).listen(3000);

You can run this server by saving the code to a file and running the following command in your terminal:

$ node hello.js

Open http://localhost:3000 in your favourite web browser to display our greeting. Exciting, isn’t it?
Of course, this is not the kind of web application we are going to get paid for. Before we can add our own business logic, we want some heavy lifting done for us, like cookie parsing, body parsing, routing, logging, and so on. This is where Express comes in. It will help you orchestrate all of this functionality, plus your business logic, and it will do it with two simple concepts: middleware functions and the middleware stack.

What are middleware functions?

In the context of an Express application, middleware functions are functions with access to the request, the response, and the next middleware in the pipeline.
As you probably noticed, middleware functions are similar to our previous HTTP handler, but with two important differences:

  • Request and Response objects are augmented by Express to expose its own API
  • Middleware has access to the next middleware in the stack

The latter leads us to the middleware stack concept. Middleware functions can be “stacked”, which means that the given two-stacked middleware functions, A and B, an incoming request will be processed by A and then by B.
In order to better understand these abstract concepts, we are going to implement a really simple middleware stack, shown here in the middleware-stack.js code listing:

'use strict';

const http = require('http');

function plusOne(req, res, next){
    // Set req.counter to 0 if and only if req.counter is not defined
    req.counter = req.counter || 0;
    req.counter++;
    return next();
}

function respond(req, res){
    res.end('req.counter value = ' + req.counter);
}

function createMiddlewareStack(/*a bunch of middlewares*/){
    var stack = arguments;

    return function middlewareStack(req, res){
        let i = 0;

        function next(){
            // pick the next middleware function from the stack and
            // increase the pointer
            let currentMiddleware = stack[i];
            if(!currentMiddleware){
                return;
            }
            i = i + 1;
            currentMiddleware(req, res, next);
        }
        // Call next for the first time
        next();
    }
}

var myMiddlewareStack = createMiddlewareStack(plusOne, plusOne, respond);

function httpHandler(req, res){
    myMiddlewareStack(req, res);
}

http.createServer(httpHandler).listen(3000);

You can run this server with the following command:

$ node middleware-stack.js

After reloading http://localhost:3000, you should read:

req.counter value = 2

Let’s analyze the code. We first define the plusOne function. This is our first middleware function and, as expected, it receives three arguments: req, res, next. The function itself is pretty simple. It ensures that the req object is augmented with the counter property, and that this property is incremented by one, then it calls the provided next() function.

The respond middleware function has a slightly different signature. The next parameter is missing. We did not include next in the signature because theres.end() function terminates the request and, therefore, there is no need to call next().

While writting a middleware function, you can either terminate the request or call next(). Otherwise, the request will hang and the client will have no response. Bear in mind that calling next or terminating the request more than once will turn into difficult errors to debug.

The createMiddlewareStack function is way more interesting than the previous ones and explains how the middleware stack works. The first statement creates a reference to the arguments object. In JavaScript, this is an Array-like object corresponding to the arguments passed to a function.

Then, we define the next() function. On each next() call, we pick a reference to the ith element of the middleware stack, which, of course, is the next middleware function on the stack. Then, we increment the value of i and pass control to the current middleware with req, res, and our recently created next function. We invoke next() immediately after its definition. The mechanism is simple: everytime next() is invoked, the value of i is incremented and, therefore, on each call, we will pass control to the next middleware in the stack.

Once our core functionality has been defined, the next steps are pretty straightforward.
myMiddlewareStack is a middleware stack composed by plusOne and respond. Then, we define a very simple HTTP handler with just one responsibility: to transfer control to our middleware stack.

Now, we have a good understanding of middleware functions and middleware stacks, and we are ready to rewrite our simple application with Express.

Install express by running the following command:

$ npm install express

Create the file express.js as follows:

'use strict';

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

function plusOne(req, res, next){
    req.counter = req.counter || 0;
    req.counter++;
    return next();
}

function respond(req, res){
    res.end('Hello from express req.counter value = ' + req.counter);
}

app.use('/', plusOne, plusOne, respond);

app.listen(3000);

app.use mounts the specified middleware/s function/s at the specified path. In our case,”/”. The path part can also be a pattern or regular expression.

Again, run this server with this command:

$ node express.js

After reloading http://localhost:3000, we should be able to read the following:

"Hello from express req.counter value = 2"

So far, we have seen how:

  • To create middleware functions and mount them at a given path
  • To make changes to request/response objects
  • To terminate a request
  • To call the next middleware

Where to go from here

Express repository is full of examples covering topics like auth, content negotiation, session, cookies, and so on. A simple restfull API can be a good project to get yourself more familiarized with the Express API: middleware, routing, and views, among others.

About the author

Pedro NarcisoGarcíaRevington is a senior full stack developer with 10+ years of experience in high scalability and availability, microservices, automated deployments, data processing, CI, (T,B,D), DD, and polyglot persistence.

LEAVE A REPLY

Please enter your comment!
Please enter your name here