# Middleware Fundamentals

Course: [Express.js](https://www.devtutorials.ai/learn/expressjs) · Section: Express Middleware
Source: https://www.devtutorials.ai/learn/expressjs/middleware/middleware-fundamentals

> What middleware is, the (req, res, next) pipeline, the three ways to finish, middleware types, global, path-based and route-specific mounting, registration order, custom middleware, access-control middleware, factories and async middleware in Express 5.

## Middleware Introduction

**Middleware** = a function that runs on a request before (or instead of) your route.

- It has three inputs: `(req, res, next)`.
- **next** = a function that passes the request to the next step.
- Middleware can read `req`, change `req` and `res`, or send an answer.
- Express runs middleware in the order you add it.
- Logging, body parsing, login checks and error pages are all middleware.

**Simple lesson:** Middleware is a checkpoint every request walks through.

### A Real-World Example

Think about the school morning line.

1. The gate guard checks your ID card.
2. The class teacher marks attendance.
3. The monitor checks your uniform.
4. Then you reach your classroom.

Any checkpoint can stop you: "No ID card, go home". Each checkpoint is middleware. The classroom is your route.

In this lesson, you will learn:

1. The shape of a middleware function.
2. The three ways middleware can finish.
3. Middleware types and where to mount them.
4. Why order matters.
5. How to write custom and reusable middleware.
6. How async middleware handles errors in Express 5.

## The Middleware Pipeline

<figure className="xfig ngc-fig">
<svg viewBox="0 0 580 180" role="img" aria-labelledby="xfig-pipe" style={{ width: "100%", maxWidth: 580, height: "auto" }}>
  <title id="xfig-pipe">Animation: a request passes a logger, a login check and a route; a second request is stopped at the login check with 401</title>
  <g style={{ fontFamily: "var(--sans)", fontSize: 12, fontWeight: 700 }}>
    <rect x="20" y="30" width="110" height="46" rx="10" style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 2 }} />
    <text x="75" y="58" textAnchor="middle" style={{ fill: "var(--ngc-purple)" }}>logger</text>
    <rect x="170" y="30" width="120" height="46" rx="10" style={{ fill: "var(--ngc-orange-soft)", stroke: "var(--ngc-orange)", strokeWidth: 2 }} />
    <text x="230" y="58" textAnchor="middle" style={{ fill: "var(--ngc-orange)" }}>requireLogin</text>
    <rect x="330" y="30" width="110" height="46" rx="10" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 2 }} />
    <text x="385" y="58" textAnchor="middle" style={{ fill: "var(--ngc-green)" }}>GET /orders</text>
    <rect x="470" y="30" width="95" height="46" rx="10" style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)", strokeWidth: 2 }} />
    <text x="517" y="58" textAnchor="middle" style={{ fill: "var(--ngc-blue)" }}>200 OK</text>
    <rect x="170" y="112" width="120" height="40" rx="10" style={{ fill: "var(--ngc-orange-soft)", stroke: "var(--ngc-orange)", strokeWidth: 2 }} />
    <text x="230" y="137" textAnchor="middle" style={{ fill: "var(--ngc-orange)" }}>401 Unauthorized</text>
  </g>
  <g style={{ fontFamily: "var(--mono)", fontSize: 11, fill: "var(--muted)" }}>
    <text x="150" y="24" textAnchor="middle">next()</text>
    <text x="310" y="24" textAnchor="middle">next()</text>
    <text x="455" y="24" textAnchor="middle">res.send</text>
    <text x="252" y="104" textAnchor="start">res.status(401)</text>
  </g>
  <circle r="9" cy="53" style={{ fill: "var(--ngc-blue)" }}>
    <animate attributeName="cx" values="75;230;385;517;517" keyTimes="0;0.15;0.3;0.45;1" dur="8s" repeatCount="indefinite" />
    <animate attributeName="opacity" values="1;1;1;1;0;0" keyTimes="0;0.15;0.3;0.45;0.5;1" dur="8s" repeatCount="indefinite" />
  </circle>
  <circle r="9" style={{ fill: "var(--ngc-orange)" }} opacity="0">
    <animate attributeName="cx" values="75;75;230;230;230" keyTimes="0;0.5;0.65;0.75;1" dur="8s" repeatCount="indefinite" />
    <animate attributeName="cy" values="53;53;53;132;132" keyTimes="0;0.5;0.65;0.75;1" dur="8s" repeatCount="indefinite" />
    <animate attributeName="opacity" values="0;0;1;1;1;0" keyTimes="0;0.49;0.5;0.75;0.95;1" dur="8s" repeatCount="indefinite" />
  </circle>
</svg>
<figcaption>Blue request: every step calls next(). Orange request: the login check answers and the trip ends.</figcaption>
</figure>

**Request-response pipeline** = the line of middleware and routes a request passes through.

- Each step either passes the request on or ends the trip.
- The order of steps is the order of your `app.use` calls.
- Routes are the last stop in the line.

**Simple lesson:** Requests flow down the pipeline until something answers.

## Three Ways to Finish

Every middleware must do one of these three things:

- **Call next()** = "I am done, pass it on".
- **Send a response** = "I answered, stop here". Use `return` so nothing else runs.
- **Call next(error)** = "Something broke". Express jumps to error middleware.
- If it does none of these, the request **hangs**. The client waits forever.

- The first request has the header, so it passes to the route.
- The second gets 401 from the middleware. The route never runs.
- `/broken` reaches the error handler and gets 500.
- `/stuck` never gets an answer.

A hanging request is hard to spot, because nothing crashes. If a client spins forever, look for middleware that forgot `next()`.

**Simple lesson:** Call `next()`, send a response, or pass an error. Never nothing.

## Middleware Types

- **Application-level:** `app.use(fn)` or `app.get(path, fn, handler)`.
- **Router-level:** `router.use(fn)`. See **Routing and Routers**.
- **Built-in:** comes with Express, such as `express.json()` and `express.static()`.
- **Third-party:** installed with npm, such as `helmet`, `cors` and `morgan`.
- **Error-handling:** has **four** inputs: `(err, req, res, next)`.

**Simple lesson:** Same idea everywhere. Only the place you mount it changes.

## Mounting Middleware

- **Global:** `app.use(fn)` runs for every request.
- **Path-based:** `app.use("/admin", fn)` runs for `/admin` and everything under it.
- **Route-specific:** `app.get("/orders", fn, handler)` runs for that one route.
- **Many at once:** `app.use(fnA, fnB)` or `app.get(path, [fnA, fnB], handler)`.
- **Middleware stack** = all the middleware in order, for one path.

- Watch the terminal for each request.
- `[global]` prints every time. The others print only on their paths.

**Simple lesson:** Mount middleware as narrowly as the job needs.

## Middleware Order

- Express runs middleware in **registration order**: the order of your code.
- Register middleware **before** the routes that need it.
- `express.json()` after a route means that route sees no body.
- Register **error middleware after** all routes.

- The answer says `"nothing"`, because the body was never parsed.
- Move `app.use(express.json())` above the route and run again.

Easy way to remember: **helpers on top, routes in the middle, errors at the bottom.**

**Simple lesson:** Middleware only helps the routes that come after it.

## Writing Custom Middleware

Middleware is just a function. Some common jobs:

- **Header check:** reject requests without an API key.
- **Feature flag:** turn a route off with one setting.
- **Request property:** add `req.requestTime` for later use.
- **Response header:** add `X-Canteen` to every answer.
- **Normalise data:** trim spaces from `req.query.search`.
- **Response locals:** put the user in `res.locals.user`.

Put each one in its own file in a `middleware/` folder, and export it.

- `searchFor` is `"dosa"`, without the extra spaces.
- Every answer carries `X-Canteen`. Check **Headers**.

**Simple lesson:** Small middleware files, each with one job.

## Access-Control Middleware

A common job: only let some users through.

- Read the user's role, for example from a header or the session.
- Compare it with the role the route needs.
- Allowed: call `next()`.
- Not logged in at all: answer **401**.
- Logged in but not allowed: answer **403**.

### Middleware Factories

**Middleware factory** = a function that **returns** middleware.

- `requireRole("staff")` returns a checker for staff.
- `requireRole("admin")` returns a checker for admins.
- One function, many configured checkers.

A header like `X-Role` is easy to fake. This lab uses it only to show the idea. Real apps read the role from a login session. See **Cookies and Sessions**.

**Simple lesson:** A factory turns one checker into many configured checkers.

## Async Middleware

Middleware can be `async` when it waits for something, like a database.

- In Express 5, if an `async` function throws, Express catches it.
- The error goes to error middleware, just like `next(err)`.
- In Express 4, you had to wrap it in `try` / `catch` yourself.
- Errors in callbacks and timers are still not caught. Use `next(err)` there.

- `/menu/9` throws inside an `async` function.
- Express 5 sends the error to the four-input middleware for you.

**Simple lesson:** In Express 5, throwing in async middleware is safe. Errors reach your error handler.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Forgetting `next()` | Express moves on by itself | You must call `next()` or respond | The request hangs |
| `res.send` then `next()` | Both can happen | Answer or pass on, not both | `ERR_HTTP_HEADERS_SENT` |
| Parser after the routes | Order does not matter | Middleware helps only later routes | `req.body` is `undefined` |
| Error middleware with 3 inputs | Any middleware can catch errors | It needs exactly 4 inputs | Errors skip it |
| Error middleware at the top | Put important things first | It must come after routes | It never sees route errors |
| Trusting a role header | Clients send honest headers | Read roles from the session | Anyone becomes admin |

</div>

## Check Yourself

## Exercises

## What to Remember

- Middleware is `(req, res, next)`, running in registration order.
- Finish with `next()`, a response, or `next(err)`. Never nothing.
- Mount it globally, on a path, on a router or on one route.
- Helpers on top, routes in the middle, error middleware at the bottom.
- Factories like `requireRole("staff")` make configurable middleware.
- Express 5 sends errors from `async` middleware to your error handler.

Next: **Body Parsers and Third-Party Middleware**. You will read JSON, forms and text from request bodies, and add packages from npm.
