What is middleware in Express?
verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
- tool
- Express
- versions
- Express 4.x – 5.x
- os
- any
Middleware in Express is a function with the signature (req, res, next) that Express calls for each matching request, in the order it was registered with app.use() or a route method. A middleware either ends the response (res.send, res.json) or calls next() to pass control to the next one. Logging, body parsing (express.json()), authentication, CORS and static files are all middleware.
Order of execution
npm init -y >/dev/null
npm install --silent --no-audit --no-fund express@5 >/dev/null 2>&1
cat > app.js <<'EOF'
const express = require("express");
const app = express();
const log = [];
app.use((req, res, next) => { log.push("1 global"); next(); });
app.use("/admin", (req, res, next) => {
log.push("2 /admin guard");
if (req.query.key !== "ok") return res.status(401).send("denied");
next();
});
app.get("/admin/stats", (req, res) => { log.push("3 route"); res.send("stats"); });
app.use((err, req, res, next) => res.status(500).send("error: " + err.message));
const server = app.listen(4402, "127.0.0.1", async () => {
for (const u of ["/admin/stats", "/admin/stats?key=ok"]) {
log.length = 0;
const r = await fetch("http://127.0.0.1:4402" + u);
console.log(u.padEnd(20), r.status, (await r.text()).padEnd(7), "|", log.join(" -> "));
}
server.close();
});
EOF
node app.jsOutput
/admin/stats 401 denied | 1 global -> 2 /admin guard /admin/stats?key=ok 200 stats | 1 global -> 2 /admin guard -> 3 route
The guard ends the first request with 401 without calling next(), so the route never runs; in the second request every layer calls next() until the route handler sends the response.
Kinds of middleware
| Kind | Registered with | Example |
|---|---|---|
| Application-level | app.use(fn), app.use("/path", fn) | request logging, auth for a path prefix |
| Router-level | router.use(fn) on an express.Router() | middleware for one group of routes |
| Route-level | app.get("/x", mw1, mw2, handler) | validation for one endpoint |
| Error-handling | app.use((err, req, res, next) => ...) with four parameters | central error responses |
| Built-in | express.json(), express.urlencoded(), express.static() | parse bodies, serve files |
| Third-party | npm packages such as cors, helmet, morgan | CORS headers, security headers, logs |
Rules that cause most bugs
- Register middleware before the routes that need it:
app.use(express.json())placed after a route leavesreq.bodyundefined in that route. - Call
next()exactly once, or send a response; doing neither leaves the request hanging until the client times out. - Error handlers need all four parameters
(err, req, res, next); with three, Express treats the function as normal middleware. - In Express 5, a rejected promise from an
asyncmiddleware or handler is passed to the error handler automatically; in Express 4 you must callnext(err)yourself.
Sources
Related pages
errors · expressExpress 5: TypeError: Missing parameter name at index 1: *Express 5 uses path-to-regexp 8, so app.get('*') throws Missing parameter name. Name the wildcard, as in /*splat or /{*splat}, or use app.use() for a fallback.changes · expressExpress 4 to 5 migration: breaking changesExpress 5 needs Node.js 18+, uses path-to-regexp 8 route syntax, forwards rejected promises to error handlers and removes old methods like res.send(status).errors · expressExpress: Error: listen EADDRINUSE: address already in use :::3000Node.js and Express fail with listen EADDRINUSE when another process already uses the port. Find it with lsof, ss or netstat, stop it, or listen on a free port.