Using middleware
Express is a routing and middleware web framework with minimal functionality of its own: an Express application is essentially a series of middleware function calls executed during the request-response cycle.
Middleware functions are functions that have access to:
- The request object (
req) - The response object (
res) - The next middleware function in the application’s request-response cycle, commonly named
next
Middleware functions can perform the following tasks:
- Execute any code.
- Modify the request and response objects.
- End the request-response cycle.
- Pass control to the next middleware function.
If a middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
An Express application can use the following types of middleware:
- Application-level middleware
- Router-level middleware
- Error-handling middleware
- Built-in middleware
- Third-party middleware
You can load application-level and router-level middleware with an optional mount path. Multiple middleware functions can also be loaded together, which creates a middleware sub-stack at a mount point.
Application-level middleware
Bind application-level middleware to an instance of the app object by using the app.use() and app.METHOD() functions, where METHOD is the lowercase HTTP method of the request that the middleware function handles, such as get, post, put, or delete.
Middleware without a mount path
The following middleware function runs every time the app receives a request:
const express = require('express');const app = express();
app.use((req, res, next) => { console.log('Time:', Date.now()); next();});import express from 'express';
const app = express();
app.use((req, res, next) => { console.log('Time:', Date.now()); next();});import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();
app.use((req: Request, res: Response, next: NextFunction) => { console.log('Time:', Date.now()); next();});Middleware mounted on a path
The following middleware function runs for any type of HTTP request on the /user/:id path:
app.use('/user/:id', (req, res, next) => { console.log('Request Type:', req.method); next();});import { type Request, type Response, type NextFunction } from 'express';
app.use('/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('Request Type:', req.method); next();});Route handlers
This example shows a route and its handler function (middleware system). The function handles GET requests to the /user/:id path:
app.get('/user/:id', (req, res) => { res.send('USER');});import { type Request, type Response } from 'express';
app.get('/user/:id', (req: Request, res: Response) => { res.send('USER');});Middleware sub-stacks
Multiple middleware functions can be loaded together at a mount point to form a middleware sub-stack. This example prints request info for any type of HTTP request to the /user/:id path:
app.use( '/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl); next(); }, (req, res, next) => { console.log('Request Type:', req.method); next(); });import { type Request, type Response, type NextFunction } from 'express';
app.use( '/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('Request URL:', req.originalUrl); next(); }, (req: Request, res: Response, next: NextFunction) => { console.log('Request Type:', req.method); next(); });Multiple route handlers
Route handlers enable you to define multiple routes for a path. The example below defines two routes for GET requests to the /user/:id path. The second route will not cause any problems, but it will never get called because the first route ends the request-response cycle.
app.get( '/user/:id', (req, res, next) => { console.log('ID:', req.params.id); next(); }, (req, res) => { res.send('User Info'); });
// handler for the /user/:id path, which prints the user IDapp.get('/user/:id', (req, res) => { res.send(req.params.id);});import { type Request, type Response, type NextFunction } from 'express';
app.get( '/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('ID:', req.params.id); next(); }, (req: Request, res: Response) => { res.send('User Info'); });
// handler for the /user/:id path, which prints the user IDapp.get('/user/:id', (req: Request, res: Response) => { res.send(req.params.id);});Skipping to the next route
Call next('route') to skip the remaining middleware functions in a router middleware stack and pass control to the next route.
Note
next('route') will work only in middleware functions that were loaded by using the
app.METHOD() or router.METHOD() functions.
In the following example, if the user ID is 0, the first handler skips to the next route, which sends a special response:
app.get( '/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass the control to the next middleware function in this stack else next(); }, (req, res) => { // send a regular response res.send('regular'); });
// handler for the /user/:id path, which sends a special responseapp.get('/user/:id', (req, res) => { res.send('special');});import { type Request, type Response, type NextFunction } from 'express';
app.get( '/user/:id', (req: Request, res: Response, next: NextFunction) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass the control to the next middleware function in this stack else next(); }, (req: Request, res: Response) => { // send a regular response res.send('regular'); });
// handler for the /user/:id path, which sends a special responseapp.get('/user/:id', (req: Request, res: Response) => { res.send('special');});Reusable middleware arrays
Middleware functions can also be grouped into arrays for better reusability. This example shows an array with a middleware sub-stack that handles GET requests to the /user/:id path:
function logOriginalUrl(req, res, next) { console.log('Request URL:', req.originalUrl); next();}
function logMethod(req, res, next) { console.log('Request Type:', req.method); next();}
const logStuff = [logOriginalUrl, logMethod];app.get('/user/:id', logStuff, (req, res) => { res.send('User Info');});import { type Request, type Response, type NextFunction } from 'express';
function logOriginalUrl(req: Request, res: Response, next: NextFunction) { console.log('Request URL:', req.originalUrl); next();}
function logMethod(req: Request, res: Response, next: NextFunction) { console.log('Request Type:', req.method); next();}
const logStuff = [logOriginalUrl, logMethod];app.get('/user/:id', logStuff, (req: Request, res: Response) => { res.send('User Info');});Router-level middleware
Router-level middleware works the same way as application-level middleware, except it is bound to an instance of express.Router().
const router = express.Router();import express from 'express';
const router = express.Router();Load router-level middleware by using the router.use() and router.METHOD() functions.
The following example code replicates the middleware system that is shown above for application-level middleware, by using router-level middleware:
const express = require('express');const app = express();const router = express.Router();
// a middleware function with no mount path. This code is executed for every request to the routerrouter.use((req, res, next) => { console.log('Time:', Date.now()); next();});
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id pathrouter.use( '/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl); next(); }, (req, res, next) => { console.log('Request Type:', req.method); next(); });
// a middleware sub-stack that handles GET requests to the /user/:id pathrouter.get( '/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass control to the next middleware function in this stack else next(); }, (req, res) => { // render a regular page res.render('regular'); });
// handler for the /user/:id path, which renders a special pagerouter.get('/user/:id', (req, res) => { console.log(req.params.id); res.render('special');});
// mount the router on the appapp.use('/', router);import express from 'express';
const app = express();const router = express.Router();
// a middleware function with no mount path. This code is executed for every request to the routerrouter.use((req, res, next) => { console.log('Time:', Date.now()); next();});
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id pathrouter.use( '/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl); next(); }, (req, res, next) => { console.log('Request Type:', req.method); next(); });
// a middleware sub-stack that handles GET requests to the /user/:id pathrouter.get( '/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass control to the next middleware function in this stack else next(); }, (req, res) => { // render a regular page res.render('regular'); });
// handler for the /user/:id path, which renders a special pagerouter.get('/user/:id', (req, res) => { console.log(req.params.id); res.render('special');});
// mount the router on the appapp.use('/', router);import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();const router = express.Router();
// a middleware function with no mount path. This code is executed for every request to the routerrouter.use((req: Request, res: Response, next: NextFunction) => { console.log('Time:', Date.now()); next();});
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id pathrouter.use( '/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('Request URL:', req.originalUrl); next(); }, (req: Request, res: Response, next: NextFunction) => { console.log('Request Type:', req.method); next(); });
// a middleware sub-stack that handles GET requests to the /user/:id pathrouter.get( '/user/:id', (req: Request, res: Response, next: NextFunction) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass control to the next middleware function in this stack else next(); }, (req: Request, res: Response) => { // render a regular page res.render('regular'); });
// handler for the /user/:id path, which renders a special pagerouter.get('/user/:id', (req: Request, res: Response) => { console.log(req.params.id); res.render('special');});
// mount the router on the appapp.use('/', router);Skipping out of a router
Use next('router') to skip the rest of the router’s middleware functions and pass control back out of the router instance.
In the following example, the router only responds when the request includes an x-auth header. Otherwise, next('router') exits the router and the app responds with a 401 status:
const express = require('express');const app = express();const router = express.Router();
// predicate the router with a check and bail out when neededrouter.use((req, res, next) => { if (!req.headers['x-auth']) return next('router'); next();});
router.get('/user/:id', (req, res) => { res.send('hello, user!');});
// use the router and 401 anything falling throughapp.use('/admin', router, (req, res) => { res.sendStatus(401);});import express from 'express';
const app = express();const router = express.Router();
// predicate the router with a check and bail out when neededrouter.use((req, res, next) => { if (!req.headers['x-auth']) return next('router'); next();});
router.get('/user/:id', (req, res) => { res.send('hello, user!');});
// use the router and 401 anything falling throughapp.use('/admin', router, (req, res) => { res.sendStatus(401);});import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();const router = express.Router();
// predicate the router with a check and bail out when neededrouter.use((req: Request, res: Response, next: NextFunction) => { if (!req.headers['x-auth']) return next('router'); next();});
router.get('/user/:id', (req: Request, res: Response) => { res.send('hello, user!');});
// use the router and 401 anything falling throughapp.use('/admin', router, (req: Request, res: Response) => { res.sendStatus(401);});Error-handling middleware
Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature (err, req, res, next):
app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!');});import { type Request, type Response, type NextFunction } from 'express';
app.use((err: Error, req: Request, res: Response, next: NextFunction) => { console.error(err.stack); res.status(500).send('Something broke!');});Caution
Error-handling middleware always takes four arguments. You must provide four arguments to
identify it as an error-handling middleware function. Even if you don’t need to use the next
object, you must specify it to maintain the signature. Otherwise, the next object will be
interpreted as regular middleware and will fail to handle errors.
Built-in middleware
Express has the following built-in middleware functions:
- express.static serves static assets such as HTML files, images, and so on.
- express.json parses incoming requests with JSON payloads.
- express.raw parses incoming requests with Buffer payloads.
- express.text parses incoming requests with text payloads.
- express.urlencoded parses incoming requests with URL-encoded payloads.
Third-party middleware
Use third-party middleware to add functionality to Express apps.
Install the Node.js module for the required functionality, then load it in your app at the application level or at the router level.
The following example illustrates installing and loading the cookie-parsing middleware function cookie-parser:
npm install cookie-parseryarn add cookie-parserpnpm add cookie-parserbun add cookie-parserconst express = require('express');const app = express();const cookieParser = require('cookie-parser');
// load the cookie-parsing middlewareapp.use(cookieParser());import express from 'express';import cookieParser from 'cookie-parser';
const app = express();
// load the cookie-parsing middlewareapp.use(cookieParser());import express, { type Express } from 'express';import cookieParser from 'cookie-parser';
const app: Express = express();
// load the cookie-parsing middlewareapp.use(cookieParser());