Node.js – Express.js Middleware
Middleware is one of the most important concepts in Express.js. A middleware function is a function that has access to the request object (req), the response object (res), and a special next function. Middleware sits between the incoming request and the final route handler — it can inspect, modify, or reject a request before it reaches its destination.
Think of middleware like a series of security checkpoints at an airport. A passenger (request) passes through check-in, then baggage screening, then passport control, and finally boards the plane (reaches the route handler). Each checkpoint can stop the passenger or let them continue. Middleware works the same way.
What Can Middleware Do?
- Execute any code before the request reaches its route handler.
- Modify the request and response objects (e.g., attach user data).
- End the request-response cycle (e.g., reject unauthorized requests).
- Call the
next()function to pass control to the next middleware or route.
Basic Middleware Structure
function myMiddleware(req, res, next) {
console.log("Middleware ran for:", req.method, req.url);
next(); // Must call next() to continue to the next handler
}
If next() is not called, the request will hang and the client will never receive a response.
Types of Middleware in Express
- Application-level middleware: Applied to the entire app using
app.use(). - Router-level middleware: Applied to a specific router using
router.use(). - Built-in middleware: Provided by Express itself (e.g.,
express.json(),express.static()). - Third-party middleware: Installed via npm (e.g.,
cors,morgan,helmet). - Error-handling middleware: A special middleware with four arguments used to handle errors.
Application-Level Middleware
Logging Middleware
const express = require('express');
const app = express();
// Logger – runs for every request
app.use(function(req, res, next) {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.url}`);
next();
});
app.get('/', function(req, res) {
res.send('Home Page');
});
app.get('/about', function(req, res) {
res.send('About Page');
});
app.listen(3000);
Every request — to any route — passes through the logger middleware first.
Applying Middleware to Specific Routes Only
function checkAge(req, res, next) {
const age = parseInt(req.query.age);
if (age >= 18) {
next();
} else {
res.status(403).send('Access denied. Must be 18 or older.');
}
}
// Apply checkAge only to this route
app.get('/adult-content', checkAge, function(req, res) {
res.send('Welcome! Access granted.');
});
Built-in Middleware
express.json() – Parsing JSON Request Bodies
app.use(express.json());
app.post('/register', function(req, res) {
const { name, email, password } = req.body;
console.log("Registering:", name, email);
res.json({ message: 'User registered successfully', name });
});
Without express.json(), req.body would be undefined for JSON requests.
express.urlencoded() – Parsing HTML Form Data
app.use(express.urlencoded({ extended: true }));
app.post('/login', function(req, res) {
const { username, password } = req.body;
res.send('Login attempt by: ' + username);
});
express.static() – Serving Static Files
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
Third-Party Middleware
morgan – HTTP Request Logger
npm install morgan
const morgan = require('morgan');
app.use(morgan('dev'));
Morgan automatically logs every request in a clean, colorful format to the terminal. This replaces the manual logger built earlier.
cors – Enable Cross-Origin Requests
npm install cors
const cors = require('cors');
app.use(cors()); // Allow requests from any origin
CORS middleware is essential when building APIs that are accessed from a different domain (e.g., a React frontend on port 5173 calling an Express backend on port 3000).
Router-Level Middleware
// routes/admin.js
const express = require('express');
const router = express.Router();
// Middleware only for admin routes
router.use(function(req, res, next) {
const isAdmin = req.headers['x-admin-token'] === 'secret123';
if (isAdmin) {
next();
} else {
res.status(401).json({ error: 'Unauthorized. Admin access only.' });
}
});
router.get('/dashboard', function(req, res) {
res.json({ message: 'Admin Dashboard loaded.' });
});
router.delete('/users/:id', function(req, res) {
res.json({ message: 'User ' + req.params.id + ' deleted by admin.' });
});
module.exports = router;
Error-Handling Middleware
Error-handling middleware in Express has exactly four parameters: err, req, res, next. It must be defined after all other routes and middleware:
const express = require('express');
const app = express();
app.get('/crash', function(req, res, next) {
const err = new Error("Something crashed!");
err.statusCode = 500;
next(err); // Pass the error to the error handler
});
// Error-handling middleware (4 parameters)
app.use(function(err, req, res, next) {
const statusCode = err.statusCode || 500;
res.status(statusCode).json({
error: err.message || 'Internal Server Error'
});
});
app.listen(3000);
Middleware Execution Order
Middleware in Express executes in the order it is defined. The sequence matters:
app.use(express.json()); // 1. Parse JSON bodies
app.use(morgan('dev')); // 2. Log requests
app.use(cors()); // 3. Handle CORS
app.use('/api/users', usersRouter); // 4. Route to handlers
app.use(errorHandler); // 5. Handle errors (last)
Key Points
- Middleware functions sit between a request and the route handler, with access to
req,res, andnext. - Always call
next()to pass control to the next middleware, unless ending the request. app.use()registers middleware for all routes; it can also target a specific path prefix.- Built-in middleware includes
express.json(),express.urlencoded(), andexpress.static(). - Third-party middleware like
morganandcorsare installed via npm and plugged in withapp.use(). - Error-handling middleware must have exactly four parameters and must be defined last.
- The order in which middleware is defined determines the order it executes.
