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:
- The first middleware logs the date, HTTP method, and URL of each request.
- The
express.json()middleware is used to automatically parse JSON-formatted request bodies. - The third middleware implements a basic form of authentication. It checks for a specific username and password in the request’s
Authorizationheader. If the credentials are not present or incorrect, it denies access and returns a 401 status code. Otherwise, it callsnext()to pass control to the next middleware. - Finally, a route handler for the root path (
/) sends back a greeting message to the authenticated user.
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
Project Management Lead. 16+ years from code to delivery. PMP®. Writing here about project management and engineering.