Express.js Middleware

Built-in, custom, and third-party middleware for secure scalable APIs

next() Built-in Third-party

Table of Contents

1. What is Middleware?

Middleware functions have access to the request object (req), the response object (res), and the next function in the application's request-response cycle.

Simple Definition

Middleware is software that acts as a bridge between the request and response cycle in Express applications.

Visual Representation

FlowClient Request → Middleware 1 → Middleware 2 → Middleware 3 → Route Handler → Response ↓ ↓ ↓ ↓ (next()) (next()) (next()) (send())

Basic Middleware Structure

JavaScriptfunction middleware(req, res, next) { // Do something, modify req/res, or end request next(); // Pass control to next middleware/route }

Simple Example

JavaScriptconst express = require('express'); const app = express(); app.use((req, res, next) => { console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`); next(); }); app.get('/', (req, res) => res.send('Home Page')); app.listen(3000);

2. How Middleware Works

The Next Function

JavaScriptapp.use((req, res, next) => { console.log('Middleware 1 - Start'); next(); console.log('Middleware 1 - End'); }); app.use((req, res, next) => { console.log('Middleware 2'); next(); }); app.get('/', (req, res) => { console.log('Route Handler'); res.send('Hello'); });

Middleware Execution Flow

JavaScriptapp.use((req, res, next) => { req.startTime = Date.now(); next(); }); app.use((req, res, next) => { req.customProperty = 'Added by middleware'; next(); }); app.use((req, res, next) => { if (req.headers['x-auth-token']) return next(); res.status(401).send('Authentication required'); }); app.get('/', (req, res) => { const responseTime = Date.now() - req.startTime; res.json({ message: 'Hello', customProperty: req.customProperty, responseTime: `${responseTime}ms` }); });

Stopping the Middleware Chain

JavaScriptapp.use((req, res, next) => { const apiKey = req.headers['x-api-key']; if (!apiKey) return res.status(401).json({ error: 'API key required' }); if (apiKey !== 'secret123') return res.status(403).json({ error: 'Invalid API key' }); next(); });

Error Handling in Middleware

JavaScriptapp.use((req, res, next) => { try { req.parsedData = JSON.parse(req.headers['x-data']); next(); } catch (error) { next(error); } }); app.use((err, req, res, next) => { res.status(500).json({ error: 'Something went wrong', message: err.message }); });

3. Types of Middleware

Classification by Scope

JavaScriptapp.use((req, res, next) => next()); // Application-level const router = express.Router(); router.use((req, res, next) => next()); // Router-level app.get('/special', (req, res, next) => next(), (req, res) => res.send('Special')); // Route-level app.use((err, req, res, next) => res.status(500).send('Error')); // Error-handling app.use(express.json()); // Built-in app.use(morgan('combined')); // Third-party

Complete Example

JavaScriptapp.use(express.json()); app.use(express.urlencoded({ extended: true })); const checkAuth = (req, res, next) => { if (!req.headers.authorization) return res.status(401).json({ error: 'No token' }); next(); }; app.get('/protected', checkAuth, (req, res) => res.json({ message: 'Protected' })); app.use((err, req, res, next) => res.status(500).json({ error: err.message }));

4. Built-in Middleware

express.json()

JavaScriptapp.use(express.json({ limit: '10mb' })); app.post('/api/users', (req, res) => res.json(req.body));

express.urlencoded()

JavaScriptapp.use(express.urlencoded({ extended: true })); app.post('/submit', (req, res) => res.json({ received: req.body }));

express.static()

JavaScriptapp.use(express.static('public')); app.use('/static', express.static('public')); app.use(express.static('public', { maxAge: '1d' }));

express.text() and express.raw()

JavaScriptapp.use(express.text()); app.use(express.raw({ type: 'application/octet-stream', limit: '2mb' }));

5. Custom Middleware

JavaScript — Loggerconst logger = (req, res, next) => { req.startTime = Date.now(); console.log(`${req.method} ${req.url}`); next(); };
JavaScript — JWT Authconst authenticateToken = (req, res, next) => { const token = req.headers['authorization']?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token' }); req.user = jwt.verify(token, process.env.JWT_SECRET); next(); };
JavaScript — Validationconst validateRequest = (schema) => (req, res, next) => { const { error } = schema.validate(req.body); if (error) return res.status(400).json({ error: 'Validation failed' }); next(); };
JavaScript — Sanitizeconst sanitizeInput = (req, res, next) => { req.body = sanitize(req.body); req.query = sanitize(req.query); next(); };

6. Third-party Middleware

Morgan — HTTP Request Logger

Bashnpm install morgan
JavaScriptconst morgan = require('morgan'); app.use(morgan('dev')); app.use(morgan('combined', { stream: accessLogStream }));

Helmet — Security Headers

JavaScriptconst helmet = require('helmet'); app.use(helmet()); app.use(helmet.hsts()); app.use(helmet.frameguard());

CORS, Compression, cookie-parser

JavaScriptapp.use(cors({ origin: 'https://myapp.com', credentials: true })); app.use(compression({ threshold: '1kb' })); app.use(cookieParser('my-secret-key')); res.cookie('user', 'john', { httpOnly: true, maxAge: 900000 });

express-rate-limit, express-session, multer

JavaScriptapp.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })); app.use(session({ secret: 'key', resave: false, saveUninitialized: false })); const upload = multer({ storage, limits: { fileSize: 5 * 1024 * 1024 } }); app.post('/upload', upload.single('image'), (req, res) => res.json({ file: req.file }));

7. Middleware Patterns

Conditional Middleware

JavaScriptconst conditionalMiddleware = (condition, middleware) => (req, res, next) => { condition(req) ? middleware(req, res, next) : next(); };

Middleware Chaining

JavaScriptapp.get('/users/:id', trackMetrics, validateId, checkCache, async (req, res) => { res.json(await db.findUser(req.id)); });

Middleware Factory Pattern

JavaScriptconst createLogger = (options = {}) => (req, res, next) => { if (!options.excludePaths?.includes(req.path)) console.log(req.url); next(); };

9. Best Practices

Order of Middleware

JavaScriptapp.use(express.json()); app.use(helmet()); app.use(cors()); app.use(morgan('combined')); app.use('/api', routes); app.use(notFoundHandler); app.use(errorHandler);

Error Handling & Security

JavaScriptconst catchAsync = (fn) => (req, res, next) => fn(req, res, next).catch(next); app.use((err, req, res, next) => res.status(err.statusCode || 500).json({ message: err.message }));

Quick Reference — Built-in

MiddlewarePurpose
express.json()Parse JSON bodies
express.urlencoded()Parse form data
express.static()Serve static files
express.text()Parse text bodies
express.raw()Parse raw bodies

Common Third-party Packages

PackagePurpose
morganHTTP logging
helmetSecurity headers
corsCORS support
compressionResponse compression
cookie-parserCookie parsing
express-rate-limitRate limiting
express-sessionSession management
multerFile uploads
Pattern Examplesapp.use((req, res, next) => next()); const middleware = (options) => (req, res, next) => next(); app.use((err, req, res, next) => res.status(500).send(err.message)); app.get('/route', [mw1, mw2, mw3], handler);