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
| Middleware | Purpose |
|---|---|
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
| Package | Purpose |
|---|---|
| morgan | HTTP logging |
| helmet | Security headers |
| cors | CORS support |
| compression | Response compression |
| cookie-parser | Cookie parsing |
| express-rate-limit | Rate limiting |
| express-session | Session management |
| multer | File 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);