Back to home

Using middleware in Express.js

Laurens Duin - June 2023

Express.js (from now on referred to as Express) is a cool framework that helps in making web applications using Node.js. One of the things I like most in Express is middleware. This paper gives a basic understanding of what middleware is and how it works in Express. We'll talk about the different types of middleware and their functions. I'll also showcase some of my own custom middleware which has helped me in projects.

Introduction to middleware

Middleware acts as a bridge between the server and the application. It allows for the execution of additional functionalities during the processing of HTTP requests. It provides a layer of code that can intercept requests, modify them, and perform various tasks such as authentication, data validation, logging, and error handling. This layer greatly improves the functionality, flexibility, and reusability of web applications.

Understanding middleware

Express.js middleware functions as a series of functions that are executed sequentially during the processing of an HTTP request. Each middleware function has access to the request object (req), response object (res), and a next function, which allows the request to proceed to the next middleware in line. There are three main types of middleware in Express.js:

By using these different types of middleware appropriately, your code can become much more consisten, scalable and maintainable.

Benefits of middleware

Middleware in Express.js offers several advantages that contribute to efficient web application development:

Custom middleware

To finish of this article, I would like to showcase some of my own custom middleware. These middleware aren't necessarily the most complicated or innovative but they've made my Express projects more scalable and pleasurable to work on.

Head data middleware

The following code is some middleware that I use in most of my Express projects. This middleware sets the right data inside res.locals for each route. Data inside res.locals will be available inside the res.render function of the route handler.


import { findRoute } from '../helpers/findRoute.js';

const setHeadData = async (req, res, next) => {
	const match = await findRoute(req.originalUrl);

	res.locals.head = data[match.route.path] ?? data.fallback;

	next();
};

const data = {
	'/new-project': {
		title: 'New Project | Appclusive',
		description: '',
		scripts: ['createProject'],
	},
	'/project/:projectId': {
		title: 'Checklist',
		description: '',
		scripts: ['checklist', 'projectinfo'],
	},
	fallback: {
		title: 'Whoopsy',
		description: 'whoops',
		scripts: [],
	},
};

export { setHeadData };

Set cache headers

The code below is taken from my server side rendere Pokédex project. This middleware checks any outgoing requests, if the request URL contains a substring, in this case pokemon, cache-control headers with a max-age are set to the request. For this specific example, this specific request is cached for 1 day.


import { Request, Response, NextFunction } from 'express';

const setCacheHeaders = (req: Request, res: Response, next: NextFunction) => {
	const timeInSeconds = 60 * 60 * 24; // 1 day

	if (req.method == 'GET' && req.path.includes('pokemon')) {
		res.set('Cache-control', `public, max-age=${timeInSeconds}`);
	}

	next();
};

export { setCacheHeaders };