Understanding Middleware Flow
1. Understanding Middleware Signature
| Arity | Signature | Type |
| 3 | (req, res, next) | Standard middleware |
| 4 | (err, req, res, next) | Error-handling middleware |
| 0/2 | — | Not recognized as middleware |
Example: Standard vs error
app.use((req, res, next) => { req.id = crypto.randomUUID(); next(); });
app.use((err, req, res, next) => { res.status(500).json({ error: err.message }); });
2. Creating Application-Level Middleware
Example: app.use()
// Runs for every request
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// Path-scoped
app.use("/api", (req, res, next) => { req.scope = "api"; next(); });
| Form | Effect |
app.use(mw) | Every request |
app.use(path, mw) | Path prefix only |
app.use(mw1, mw2) | Multiple stacked |
3. Creating Router-Level Middleware
Example: Router scope
const router = Router();
router.use(requireAuth);
router.use((req, res, next) => { req.tenant = req.user.tenant; next(); });
| Scope | Method |
| App | app.use() |
| Router | router.use() |
| Route | app.get(path, mw, handler) |
4. Using Built-in Middleware
| Middleware | Purpose |
express.json([opts]) | Parse JSON bodies |
express.urlencoded([opts]) | Parse application/x-www-form-urlencoded |
express.raw([opts]) | Buffer body (e.g., webhooks) |
express.text([opts]) | Plain text body |
express.static(root, [opts]) | Static files |
express.Router([opts]) | Mountable route group |
5. Implementing Third-Party Middleware
| Package | Role |
helmet | Security headers |
cors | CORS |
compression | gzip/brotli |
morgan | HTTP request logger |
cookie-parser | Cookie parsing |
express-session | Sessions |
express-rate-limit | Rate limiting |
passport | Authentication |
multer | File uploads |
6. Creating Custom Middleware Functions
Example: Request ID + duration
export function requestId() {
return (req, res, next) => {
req.id = req.headers["x-request-id"] || crypto.randomUUID();
res.setHeader("X-Request-Id", req.id);
next();
};
}
export function duration(logger) {
return (req, res, next) => {
const start = process.hrtime.bigint();
res.on("finish", () => {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
logger.info({ id: req.id, ms, status: res.statusCode });
});
next();
};
}
app.use(requestId(), duration(logger));
7. Using Middleware for Specific Routes
Example: Per-route stacks
app.post("/upload", auth, multer().single("file"), validateFile, (req, res) => {
res.json({ filename: req.file.originalname });
});
| Pattern | Use |
| Inline | Specific to one route |
| Array | Reusable group: const adminMW = [auth, requireAdmin] |
8. Executing Multiple Middleware
┌──────┐ next() ┌──────┐ next() ┌─────────┐
req ──▶ │ MW 1 │ ───────▶ │ MW 2 │ ───────▶ │ Handler │ ──▶ res
└──────┘ └──────┘ └─────────┘
│
└─ next(err) ──▶ Error MW
9. Calling next() Function
| Call | Effect |
next() | Pass to next middleware |
next(err) | Skip to error handler |
next("route") | Skip to next matching route |
next("router") | Skip remaining router middleware |
| (no call) | Hangs forever — must terminate response |
Warning: Calling next() AND res.send() in the same middleware causes "Cannot set headers after they are sent" errors.
10. Skipping Remaining Middleware
Example: next("route") to bypass
app.get("/users/:id",
(req, res, next) => {
if (req.params.id === "0") return next("route"); // skip handler below
next();
},
(req, res) => res.json({ id: req.params.id })
);
app.get("/users/:id", (req, res) => res.json({ id: "fallback" }));
11. Understanding Middleware Order
| Rule | Detail |
| Top-down | Order of app.use() calls determines execution |
| Body parsers first | Before any route that reads req.body |
| Static last | Before 404 handler |
| Error handler LAST | Must be after all routes |