Middleware Pipeline
Overview
Middleware is the absolute core architecture of Express.js. When a request hits your server, it doesn't just go to the route handler instantly. It travels through a sequential pipeline of functions (Middleware). Each function can intercept the request, inspect it (e.g., 'Is there a valid JWT token?'), mutate it (e.g., 'Parse the JSON body'), and then explicitly decide whether to pass it to the next() function in the chain, or reject it entirely.
Syntax
const express = require('express');
const app = express();
// 1. A Custom Middleware Function
// It MUST take 3 arguments: req, res, next
const requireAuth = (req, res, next) => {
const token = req.headers['authorization'];
if (token === 'secret_password') {
// Validation Passed! Push the request to the next function in line!
next();
} else {
// Validation Failed! We intercept and end the stream immediately.
// The actual route handler will NEVER be executed.
res.status(401).json({ error: "Unauthorized access!" });
}
};
// 2. Applying Middleware to a Route
// The request must survive 'requireAuth' before it reaches the final callback!
app.get('/api/dashboard', requireAuth, (req, res) => {
res.json({ secretData: "Welcome to the admin panel." });
});
// 3. Applying Global Middleware (Runs on EVERY request)
app.use(express.json()); // Parses all incoming JSON bodies nativelyCommon Pitfalls
- Forgetting to call
next(). If a middleware function does not send a response (res.json) AND forgets to callnext(), the HTTP request permanently hangs in limbo. The client's browser will spin forever until it times out. You must guarantee that every logical path terminates the request or passes it on. - Order of declaration.
app.use()executes in strict top-to-bottom order. If you define your routes first, and then declareapp.use(requireAuth)at the bottom of the file, the authentication will never run, leaving your API completely exposed.
Interview Questions
Middleware can freely mutate the req object. If an authentication middleware verifies a JWT token, it can extract the user's database ID and attach it via req.userId = 42. When next() is called, the final route handler can instantly access req.userId without having to parse the token again.
Real-World Example
A simple logging middleware that records the exact milliseconds every API request takes to execute.
const logger = (req, res, next) => {
const start = Date.now();
// We attach an event listener to the response stream!
// When the stream officially finishes sending data, it fires.
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${req.method}] ${req.url} - ${duration}ms`);
});
next(); // Immediately push the request forward!
};
app.use(logger);Check Your Knowledge
Test your understanding of Middleware Pipeline with these quick questions.