Menu

Express Middleware: How next() Works, With Examples

Express middleware is a function (req, res, next) that runs before your route handler. It can read or change the request, end it with a response, or call next() to pass it on.

This page includes runnable editors. Edit, run, and see output instantly.

Middleware in Express is a function with the signature (req, res, next) that runs between the moment a request arrives and the moment a response is sent. Each middleware can read or change req and res, end the request by sending a response, or call next() to hand the request to the next function in line. app.use(fn) adds middleware for every request, and Express runs everything in the order you registered it.

The Console logs the three steps in order for every request, and the route reads req.startedAt, a property the logger added. Send GET /missing: the two middleware still log, because app.use() matches every path, and then Express answers 404 since no route matched.

Forgetting next()

A middleware that neither sends a response nor calls next() leaves the request hanging. The client waits until it times out, and nothing appears in the logs, which makes this a common middleware bug. Delete next(); from logger above and send GET / again: the request never gets an answer.

The rule is simple: every path through a middleware ends in exactly one of these:

  • next() to continue,
  • next(err) to jump to the error handler (see Express error handling),
  • a response such as res.json() or res.status(401).send().

Calling next() after sending a response is also a bug. The next handler tries to send a second response and Express throws ERR_HTTP_HEADERS_SENT. Write return res.status(401).json(...) so the function stops there.

An auth check that stops the request

Middleware that rejects a request sends the response itself and never calls next(). Pass it as an extra argument to a route to protect only that route:

The first request gets 401 with {"error":"Missing or wrong API key"}. Add the header x-api-key: secret123 in the request bar and send it again: now the answer is {"users":42,"caller":"reporting-app"}. /public never runs the check.

Where middleware runs

You writeRuns for
app.use(fn)Every request, every method
app.use('/api', fn)Paths that start with /api
app.get('/admin', fn, handler)GET /admin only, before handler
router.use(fn)Every route of that router

Order is part of the meaning. A logger registered after the routes never logs requests those routes answered, because a route that sends a response does not call next(). Register app-wide middleware such as logging, body parsing and CORS first, then the routes, then the 404 and error handlers.

Built-in middleware

Express ships a few middleware functions you add with app.use(). The body parsers fill req.body; without them it is undefined.

MiddlewareWhat it does
express.json()Parses JSON bodies (Content-Type: application/json)
express.urlencoded()Parses HTML form posts (name=Ada&age=36)
express.text()Puts a plain text body in req.body as a string
express.static('public')Serves files from a folder

Send POST /echo with the body {"name": "Ada"} and the answer includes "body":{"name":"Ada"}. Remove the express.json() line, send it again, and body is missing from the answer: req.body is undefined, and JSON.stringify leaves undefined values out. Packages such as cors, helmet and morgan are middleware too: you npm install them and pass them to app.use() the same way. The express.static page covers serving files.

Common mistakes

Calling the middleware instead of passing it. app.use(logger()) runs logger once at startup and registers its return value. Write app.use(logger). (express.json() has parentheses because it returns a middleware function.)

Adding the body parser after the routes. The routes run first and see req.body as undefined.

Expecting app.use('/admin', fn) to match one path. It also runs for /admin/users and every other path below /admin, for every method. To guard a single route, pass the middleware to app.get('/admin', fn, handler).

Frequently Asked Questions

What is the difference between middleware and a route handler?

Technically nothing: both are functions of (req, res, next). By convention a route handler ends the request with a response, while middleware usually does a shared job and calls next().

Can middleware be async?

Yes. In Express 5 an async middleware that throws or returns a rejected promise sends the error to your error handler automatically. Await what you need, then call next().

What does next('route') do?

It skips the remaining handlers of the current route and moves to the next route that matches. It works only in handlers added with app.get(), app.post() and the other method functions, not in app.use().

How do I skip a middleware for one route?

Register that route before the middleware, so it answers first, or check the path inside the middleware (if (req.path === '/health') return next();). Applying the middleware per router or per route instead of app-wide often removes the need.

Coddy programming languages illustration

Learn to code with Coddy

Get started