Skip to content

ExpressJS - Middleware

Middleware functions are the heart of Express.js. They are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. Middleware can execute any code, make changes to the request and response objects, end the request-response cycle, or call the next middleware in the stack.

Think of middleware as a series of checkpoints that a request must pass through. At each checkpoint, you can inspect, modify, or even reject the request.

Express middleware can be categorized into several types:

  • Application-level middleware: Bound to the app object using app.use() or app.METHOD().
  • Router-level middleware: Works the same as application-level, but is bound to an instance of express.Router().
  • Error-handling middleware: Has a special signature of (err, req, res, next) to handle errors.
  • Built-in middleware: Standard middleware functions included with Express, such as express.json() and express.static().
  • Third-party middleware: Middleware available as packages on npm, like helmet or morgan.

Let’s create a simple logger middleware that prints the timestamp and method for each incoming request.

import express from 'express';
const app = express();
const PORT = 3000;
// Custom logger middleware
const requestLogger = (req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.originalUrl}`);
// This function call is crucial! It passes control to the next middleware.
// Without it, the request would be left hanging.
next();
};
// Apply the middleware to all routes
app.use(requestLogger);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/about', (req, res) => {
res.send('This is the about page.');
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

When you run this server and visit http://localhost:3000/, your console will show:

Server is running on http://localhost:3000
[2023-10-27T10:30:00.123Z] GET /

You can apply middleware to a specific route or a group of routes by providing a path as the first argument to app.use().

const adminAuth = (req, res, next) => {
// In a real app, you'd check for authentication credentials
console.log('Admin auth check passed!');
next();
};
// This middleware will only run for requests to /admin and its sub-routes
app.use('/admin', adminAuth);
app.get('/admin/dashboard', (req, res) => {
res.send('Welcome to the Admin Dashboard');
});

Middleware functions are executed sequentially, so the order in which you include them is critical. A request will pass through them one by one.

// Middleware 1: First to execute
app.use((req, res, next) => {
console.log('First Middleware: Setting request time');
req.requestTime = Date.now();
next();
});
// Middleware 2: Second to execute
app.use((req, res, next) => {
console.log('Second Middleware: Logging request time');
console.log(`Request received at: ${new Date(req.requestTime).toLocaleTimeString()}`);
next();
});
app.get('/', (req, res) => {
res.send(`Request was processed.`);
});

If a middleware function does not call next(), it must terminate the request-response cycle itself (e.g., by calling res.send() or res.json()). Otherwise, the request will be stuck.

Built-in and Common Third-Party Middleware

Section titled “Built-in and Common Third-Party Middleware”

Express comes with powerful built-in middleware, and the npm ecosystem offers countless more for common tasks.

Built-in: express.json() and express.urlencoded()

Section titled “Built-in: express.json() and express.urlencoded()”

Previously, the body-parser package was required to parse incoming request bodies. This functionality is now built directly into Express.

// To parse JSON-formatted request bodies
app.use(express.json());
// To parse URL-encoded request bodies (e.g., from HTML forms)
app.use(express.urlencoded({ extended: true }));
app.post('/api/users', (req, res) => {
// req.body is now populated with the parsed data
const newUser = req.body;
console.log(newUser);
res.status(201).json(newUser);
});

This middleware parses the Cookie header and populates req.cookies with an object keyed by the cookie names.

# Installation
npm install cookie-parser
# Usage
import cookieParser from 'cookie-parser';
app.use(cookieParser());
app.get('/set-cookie', (req, res) => {
res.cookie('username', 'john.doe', { httpOnly: true });
res.send('Cookie has been set!');
});
app.get('/read-cookie', (req, res) => {
// Access the cookie from req.cookies
const username = req.cookies.username;
res.send(`Hello, ${username}!`);
});

morgan is a popular HTTP request logger middleware. It’s more powerful than our custom logger and is highly configurable for production environments.

# Installation
npm install morgan
# Usage
import morgan from 'morgan';
// Use the 'dev' format for concise, color-coded logs during development
app.use(morgan('dev'));