Middleware is a function that runs before a route's handler, with the chance to inspect or modify the request, and then either pass it along or stop it there. It's how Express handles anything that needs to happen on more than one route without repeating the code in every handler.
A middleware function takes three parameters — req, res, and one more: next.
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`)
next()
}
app.use(logger)Calling next() passes control to whatever comes after — the next middleware, or the matching route's own handler. Forget to call it, and the request just hangs — the browser waits forever for a response that never comes.
Express ships with some middleware already included. express.json() is the most common — it reads a JSON request body and makes it available as req.body. The next lesson covers it in full.
app.use(express.json())app.use() applies to every route that follows it. To scope middleware to a single route, pass it as an extra argument:
function requireLogin(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send('Login required')
}
next()
}
app.get('/dashboard', requireLogin, (req, res) => {
res.send('Welcome to your dashboard')
})This is why route handlers themselves take the same (req, res) shape — a route handler is really just the last middleware in the chain, the one that doesn't call next() and instead sends the response.
A middleware function with four parameters instead of three is treated specially by Express — it only runs when something earlier calls next(error) or throws:
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something went wrong')
})This kind of middleware always goes last, after every route — Express matches middleware in the order it's registered, so an error handler placed earlier would never see requests that come after it.