Jan 22, 2023

A Beginner's Guide to Express Middlewares

A Beginner's Guide to Express Middlewares

Since Express is, by design, very minimalistic in terms of functionalities, the framework offers the possibility for developers to intercept an incoming HTTP request before it reaches the endpoint’s controller using a special type of function called a middleware.

These middlewares can be used for a variety of actions such as logging a request, verifying its headers, or parsing its payload, and can be chained one after the other in order to form what’s called a middleware stack.

The Request-Response Lifecycle

Just like any other request listener, a middleware function has access to the request and response objects, as well as an additional parameter named next.

function middleware(req, res, next) {
  //
}

It can terminate the request-response lifecycle by sending an immediate response to the client using one of the methods of the response object, such as send(), sendStatus(), and so on.

Request-Response Lifecycle 1

For example:

function middleware(req, res, next) {
  // Execute internal logic
  res.sendStatus(200);
}

It can forward the request to the next middleware (or controller) in the stack, by invoking its next() argument — as without it, the request will be left hanging until it times out.

Request-Response Lifecycle 2

For example:

function middleware(req, res, next) {
  // Execute internal logic
  next();
}

Note that any modifications made to the request and response objects — like the addition of a new property — will be preserved, and passed on to each component of the middleware stack.

Connecting Middlewares

In Express, a middleware function can be declared in two different ways:

  • At the application-level.
  • At the route-level.

Application-level middleware

An application-level middleware is a function that is bound to the application object through its use() method:

app.use([PATH,] CALLBACK [, CALLBACK...])

Where:

  • PATH represents an optional mount path, a path pattern, a regular expression or an array of combinations of these three.
  • CALLBACK represents a single middleware function or a series of middleware functions.

In this example, every single request sent to the application will first pass through the logger() middleware function before being routed to the appropriate request listener.

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

const logger = (req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
};

const controller = (req, res) => res.sendStatus(200);

app.use(logger);

app.get('/foo', controller);

app.get('/bar', controller);

app.listen(8080);

You can try it yourself by running the following commands:

$ curl 127.0.0.1:8080/foo
GET /foo

$ curl 127.0.0.1:8080/bar
GET /bar

Route-level middleware

A route-level middleware is a function that is bound to a specified route by passing it as an argument of this route, between the path string and the controller.

app.METHOD(PATH, MIDDLEWARE[, MIDDLEWARE], HANDLER);

Where:

  • METHOD is a function representing an HTTP method.
  • PATH is a string of characters representing a URN.
  • MIDDLEWARE is a middleware function.
  • HANDLER is a request listener function.

In this example, only the requests sent to the GET /foo endpoint will first pass through the logger() middleware function before being routed.

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

const logger = (req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
};

const controller = (req, res) => {
  res.sendStatus(200);
};

app.get('/foo', logger, controller);

app.get('/bar', controller);

app.listen(3000);

You can try it yourself by running the following commands:

$ curl 127.0.0.1:8080/foo
GET /foo

$ curl 127.0.0.1:8080/bar

Related posts