# Middleware

## 1. What Middleware Is

**Middleware** is a function that sits between the server receiving a request and your route handler running. Each middleware gets the request, can read or modify it, and then either **passes control on** (`next()`) or **ends the request** itself by sending a response.

```ts
// Express signature: (req, res, next)
function requestTimer(req: Request, res: Response, next: NextFunction) {
  const start = Date.now();
  res.on("finish", () => console.log(`${req.method} ${req.url} ${res.statusCode} ${Date.now() - start}ms`));
  next(); // hand off to the next middleware / the route handler
}

app.use(requestTimer);
```

Middleware is how cross-cutting concerns — things every (or most) routes need — are written **once** instead of copy-pasted into every handler: logging, CORS, body parsing, authentication (`12-authentication_authorization/`), rate limiting, compression, request IDs.

## 2. The Chain: Order Is Everything

Middleware runs **in registration order**. Each one decides whether the request continues:

```ts
app.use(express.json());          // 1. parse body -> req.body
app.use(requestId);               // 2. attach req.id
app.use(requestTimer);            // 3. start timing
app.use("/admin", requireAdmin);  // 4. only for paths under /admin
app.get("/admin/stats", statsHandler);
```

- A middleware that calls `next()` passes control **down** the chain.
- A middleware that sends a response (`res.status(401).json(...)`) and does **not** call `next()` **short-circuits** — nothing after it runs.
- A middleware that does neither leaves the request **hanging** until the client times out.

Because order matters, putting `requireAdmin` *before* `express.json()` means it can't see the body; putting a logger *after* the router means it never sees requests the router already answered.

## 3. The "Onion": Code Before and After `next()`

When `next()` returns (or its promise resolves, in Koa/Nest/async-style frameworks), the downstream handler has finished. Code after `next()` therefore runs on the way **out**:

```ts
// Koa-style async middleware
app.use(async (ctx, next) => {
  const start = Date.now();       // inbound: before the handler
  await next();                   // everything downstream runs here
  ctx.set("X-Response-Time", `${Date.now() - start}ms`); // outbound: after the handler
});
```

This gives every middleware two hooks — before and after — around everything nested inside it, like the layers of an onion. Express's classic callback `next()` doesn't return a promise you can await, which is why Express code uses `res.on("finish")` for "after" logic instead.

## 4. Error-Handling Middleware

Express recognizes middleware with **four** parameters as an error handler. Calling `next(err)` (or throwing, in Express 5 async handlers) skips all regular middleware and jumps to the next error handler:

```ts
app.get("/orders/:id", async (req, res, next) => {
  try {
    res.json(await ordersService.findById(req.params.id));
  } catch (err) {
    next(err); // skip to error-handling middleware
  }
});

// registered LAST, after all routes
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  res.status(500).json({ error: "internal error" });
});
```

`11-exception_handling/` builds the full mapping of error types to status codes on top of this.

## 5. Scoping: Global, Path, Router, Route

```ts
app.use(cors());                          // global: every request
app.use("/api", apiRateLimiter);          // path-scoped: /api/**
const admin = express.Router();
admin.use(requireAdmin);                  // router-scoped: only routes on this router
admin.get("/stats", statsHandler);
app.use("/admin", admin);
app.post("/upload", multerSingle("file"), uploadHandler); // route-level: just this route
```

Apply middleware at the **narrowest** scope that's correct: an expensive multipart parser belongs on the upload route only, not globally.

## 6. NestJS: Middleware vs Guards vs Interceptors vs Pipes

Nest splits Express's single "middleware" idea into specialized stages that run in a fixed order:

| Stage | Runs | Typical job | Knows the target handler? |
|---|---|---|---|
| Middleware | first | raw req/res work: logging, CORS, request IDs | no |
| Guards | after middleware | **may this request proceed?** (auth, roles) | yes (`ExecutionContext`) |
| Interceptors (before) | after guards | timing, caching, transforming input | yes |
| Pipes | right before the handler | validate/transform params (`08-validation_and_dtos/`) | yes |
| Handler | — | the controller method | — |
| Interceptors (after) | after the handler | map/wrap the response | yes |
| Exception filters | on throw | turn errors into responses | yes |

The practical difference: a **guard** knows which controller method is about to run (and can read its `@Roles()` metadata); plain middleware doesn't. `07-request_response_lifecycle/` walks a request through this whole pipeline.

## Under the Hood: How `next` Is Implemented

Express keeps an array of **layers** (each with a path matcher and a handler). Dispatch is a tiny recursive function closed over an index:

```ts
function handle(req, res, stack, done) {
  let idx = 0;
  function next(err?) {
    const layer = stack[idx++];
    if (!layer) return done(err);                    // ran off the end -> 404 / final handler
    if (!layer.matches(req.path)) return next(err);  // not for this path, skip
    const isErrorHandler = layer.handle.length === 4; // arity check!
    if (err && !isErrorHandler) return next(err);    // in error mode, skip normal middleware
    if (!err && isErrorHandler) return next();       // in normal mode, skip error handlers
    try {
      err ? layer.handle(err, req, res, next) : layer.handle(req, res, next);
    } catch (e) {
      next(e);                                       // sync throw -> error mode
    }
  }
  next();
}
```

Three consequences fall straight out of this:
1. **Arity matters.** An error handler written `(err, req, res) => ...` (3 params) is *not* treated as an error handler, because Express checks `fn.length === 4`.
2. **"Calling next twice" is a real bug.** Each call advances `idx` — a middleware that calls `next()` and then also sends a response will run the rest of the chain *and* respond, causing "Cannot set headers after they are sent."
3. **Composition is just function nesting.** Koa's `koa-compose` does the same with `dispatch(i) => Promise.resolve(fn(ctx, () => dispatch(i + 1)))`, which is why `await next()` naturally gives you the onion.

![A request flows inward through each middleware's before-next code to the handler, then back outward through each middleware's after-next code; a middleware that responds without calling next short-circuits the rest](diagrams/01-middleware-onion.svg)

## Common Pitfalls

1. **Forgetting to call `next()`** (and not responding) — the request hangs until timeout.
2. **Calling `next()` and also sending a response** — "Cannot set headers after they are sent to the client."
3. **Wrong order** — auth after the route, body parser after a middleware that reads the body, error handler registered before routes.
4. **Error handler with 3 parameters** — silently treated as normal middleware.
5. **Unhandled promise rejections in Express 4 async middleware** — a rejected promise is not routed to `next(err)` automatically; wrap handlers or upgrade to Express 5.
6. **Global heavy middleware** — parsing multipart or decompressing bodies on every route wastes CPU; scope it.

## Quick Reference

| Need | Do |
|---|---|
| run for every request | `app.use(fn)` |
| run for a path prefix | `app.use("/api", fn)` |
| run for one route | `app.get("/x", fn, handler)` |
| continue | `next()` |
| stop with a response | `res.status(...).json(...)` and **don't** call `next()` |
| raise an error | `next(err)` |
| handle errors | `app.use((err, req, res, next) => ...)` — 4 params, registered last |
| "after" logic | `res.on("finish", ...)` (Express) / code after `await next()` (Koa/Nest) |
