Node.js SeriesMarch 28, 2024 · 2 min read

Middleware Pattern in Node.js

If you have written any Express code, you have used this pattern daily without ceremony. What is worth learning is the shape itself, a pipeline of small functions each owning one concern, because it applies far beyond HTTP: I have used the same structure for order-processing pipelines and validation chains.

This post is part of my Node.js Design Patterns series.

The Middleware Pattern is a structural design pattern commonly used in web development, especially in web servers and applications. It allows developers to add layers of processing to requests and responses, making the code more modular, reusable, and easier to maintain. Middleware functions can perform a variety of tasks such as logging, authentication, data parsing, and error handling before sending the response to the client or passing the request to another middleware function.

Here’s a simple example of the Middleware Pattern in a Node.js application using Express.js, a popular web application framework. This example demonstrates how middleware can be used for logging requests, parsing JSON payloads, and implementing basic authentication.

Step 1: Set Up Your Project

First, you need to set up a Node.js project and install Express:

mkdir express-middleware-examplecd express-middleware-examplenpm init -ynpm install express

Step 2: Create Your Server with Middleware

Create a file named server.js and add the following code to demonstrate the Middleware Pattern:

const express = require('express'); const app = express(); const PORT = 3000; // Middleware for logging requests app.use((req, res, next) => {   console.log([${new Date().toISOString()}] ${req.method} ${req.url});   next(); // Pass control to the next middleware }); // Built-in middleware for parsing JSON bodies app.use(express.json()); // Middleware for basic authentication app.use((req, res, next) => {   const auth = { login: 'admin', password: 'secret' }; // Example credentials const b64auth = (req.headers.authorization || '').split(' ')[1] || '';   const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':'); // Verify login and password are set and correct   if (login && password && login === auth.login && password === auth.password) {     // Access granted…     return next();   } // Access denied…   res.set('WWW-Authenticate', 'Basic realm="401"') // Change this as required.     .status(401).send('Authentication required.'); // Custom message. }); // Route handler as the final "middleware" app.get('/', (req, res) => {   res.send('Hello, World! You are authenticated.'); }); app.listen(PORT, () => console.log(Server running on http://localhost:${PORT}));

Step 3: Run Your Server

Run your server using Node.js:

node server.js

In this example:

This setup showcases how middleware functions can be chained together to process requests and how each middleware can perform its specific task, either modifying the request/response objects, terminating the request-response cycle, or calling the next middleware in the stack.

Nguyễn Hải Nam

Nguyễn Hải Nam

Project Management Lead. 16+ years from code to delivery. PMP®. Writing here about project management and engineering.

About me