---
term: 'Middleware (Request Lifecycle)'
seoTitle: 'Middleware Explained: Request Pipeline, Order Bugs, next()'
headline: 'What is Middleware (Request Lifecycle)?'
slug: middleware
category: backend-compute
shortDefinition: 'Middleware is a function in the request pipeline that inspects or modifies requests and responses before your route logic runs.'
relatedTerms:
  - api-gateway-architecture
  - database-triggers-beforesave-aftersave
  - cloud-code-serverless-functions
  - api
contrastsWith:
  - api-gateway-architecture
aboutTerms:
  - 'Request Pipeline'
  - 'next()'
  - 'Error-Handling Middleware'
faq:
  - question: 'What is middleware in simple terms?'
    answer: 'A function that sits in the path between an incoming request and your route logic, processing every request on the way through — like airport security checkpoints before the gate. Each one inspects or modifies the request, then passes it along or stops it cold.'
  - question: 'What are common middleware examples?'
    answer: 'The usual stack: security headers, CORS, body parsing with size limits, logging, authentication, authorization, rate limiting, static file serving, and — at the end — 404 and error handlers. Almost everything cross-cutting in a web app is middleware.'
  - question: 'How does the middleware chain work?'
    answer: 'Each function either ends the cycle by sending a response or calls next() to pass control to the following one; the framework walks the stack in registration order until something responds. The classic bug: neither responding nor calling next() — the request hangs forever.'
  - question: 'Does middleware order matter?'
    answer: 'It is the number-one bug source. Authorization before authentication checks permissions against nobody; a body parser after the routes leaves req.body undefined; auth before CORS makes browsers mask the real error; an error handler anywhere but last swallows nothing. Order is the program.'
  - question: 'What is error-handling middleware?'
    answer: 'A middleware the framework routes errors to instead of the normal chain — in Express, recognized by its four-argument signature (err, req, res, next) and registered last. Thrown errors and next(err) calls skip everything else and land there, which is why its position is non-negotiable.'
  - question: 'What is the difference between middleware and a route handler?'
    answer: 'Intent and position. Middleware handles cross-cutting concerns for many routes and usually passes control on; the route handler is the destination that produces the response. In most frameworks they are structurally identical functions — the pipeline just ends at one of them.'
  - question: 'What is the difference between middleware and an API gateway?'
    answer: 'Scope. Middleware runs inside one application''s process, per request; a gateway is infrastructure in front of many applications, handling routing, auth, and rate limits across services. A gateway is middleware for your whole architecture — and they compose rather than compete.'
  - question: 'Is middleware the same in every framework?'
    answer: 'Same concept, different spelling: Express passes (req, res, next); Django middleware wraps get_response in an onion; Rack apps call the next app with env; Koa awaits next() so code after it runs on the response''s way back out. Learn the model once and every framework is an accent.'
  - question: 'What about the older "enterprise middleware" meaning?'
    answer: 'The 1980s sense — message brokers, integration buses, application servers gluing separate systems together. Same word, different layer: that middleware sits between applications; request-pipeline middleware sits between a request and a response inside one application. Modern web usage almost always means the second.'
  - question: 'When should middleware NOT call next()?'
    answer: 'When it has fully handled the request: an auth rejection returning 401, a rate limiter returning 429, a cache hit, a redirect, a CORS preflight response. Short-circuiting is the feature — the guarantee that nothing past the gate runs for requests that failed it.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Using middleware — Express'
    url: 'https://expressjs.com/en/guide/using-middleware.html'
  - name: 'Middleware — Django documentation'
    url: 'https://docs.djangoproject.com/en/5.2/topics/http/middleware/'
  - name: 'Rails on Rack — Ruby on Rails Guides'
    url: 'https://guides.rubyonrails.org/rails_on_rack.html'
  - name: 'Middleware — MDN Web Docs glossary'
    url: 'https://developer.mozilla.org/en-US/docs/Glossary/Middleware'
  - name: 'Middleware — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Middleware'
cta:
  title: 'The stack, already stacked'
  text: 'Back4app runs the production middleware gauntlet for every request — headers, CORS, parsing, auth, rate limits — and gives you Cloud Code triggers as the clean place for your custom per-request logic.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-30'
translationKey: middleware
---

**Middleware is a function in the request pipeline that inspects or modifies requests and responses before your route logic runs.** Two senses share the word — the older enterprise sense (message brokers and integration buses *between applications*) and the web-framework sense this entry covers: functions *inside* one application that every request flows through, in order. Express states the model plainly: an app "is essentially a series of middleware function calls" — and the order of those calls is, quite literally, the program.

## Key takeaways

| Question | Answer |
| --- | --- |
| The contract | Inspect/modify → then respond (short-circuit) **or** call `next()` |
| The shape | An onion: requests pass down the stack, responses bubble back up |
| The law | Registration order = execution order — most middleware bugs are order bugs |
| The canonical stack | Headers → CORS → parsing → logging → authn → authz → limits → routes → 404 → errors |
| vs. the gateway | Middleware runs *inside* one app; a [gateway](/glossary/api-gateway-architecture/) fronts many |

## The stack, in order

**JavaScript:**

```javascript
// JavaScript / Node.js — Express + Parse Server
// Middleware: functions the request flows through, in registration order
const app = express();
app.use(helmet());                       // 1 · security headers
app.use(cors(corsOptions));              // 2 · CORS before anything that fails
app.use(express.json({ limit: '1mb' })); // 3 · body parsing, bounded

// Parse Server IS middleware — a whole backend mounted into the stack
app.use('/parse', new ParseServer(config).app);

app.use(notFoundHandler);                // 404 — after all routes
app.use(errorHandler);                   // error handler LAST (4 args)
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// One SDK call — and the platform's middleware stack ran the gauntlet:
final response =
    await QueryBuilder<ParseObject>(ParseObject('Post')).query();
// Before your query touched data, the request passed through:
//   security headers → CORS → body limits → key check → session auth
//   → rate limiting → routing → (your beforeFind trigger) → the database
// You wrote none of it — that's the middleware a BaaS runs for you.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// One SDK call — and the platform's middleware stack ran the gauntlet:
let posts = try await Post.query().find()
// Before your query touched data, the request passed through:
//   security headers → CORS → body limits → key check → session auth
//   → rate limiting → routing → (your beforeFind trigger) → the database
// You wrote none of it — that's the middleware a BaaS runs for you.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// One SDK call — and the platform's middleware stack ran the gauntlet:
val posts = ParseQuery.getQuery<ParseObject>("Post").find()
// Before your query touched data, the request passed through:
//   security headers → CORS → body limits → key check → session auth
//   → rate limiting → routing → (your beforeFind trigger) → the database
// You wrote none of it — that's the middleware a BaaS runs for you.
```

Each position has a reason: security headers first (they must be on *every* response, including errors); CORS before anything that can fail (or browsers mask the real error); body parsing bounded and before routes (or `req.body` is undefined); [authentication before authorization](/glossary/authentication-vs-authorization/) (permissions checked against nobody are permissions granted to anybody); [rate limiting](/glossary/api-rate-limiting-throttling/) before expensive work (a limiter after the database query protects nothing); the 404 after all routes; the error handler dead last.

## The onion, drawn correctly

```mermaid
flowchart LR
  accTitle: Middleware onion with request descending and response ascending
  accDescr: A request passes inward through each middleware layer in registration order until it reaches the route handler at the core, and the response then travels back outward through the same layers in reverse order, letting each middleware act twice — once on the way in and once on the way out. A layer may short-circuit, sending a response before inner layers ever run.
  RQ["Request"] --> M1["Headers / CORS"]
  M1 --> M2["Auth"]
  M2 --> M3["Rate limit"]
  M3 --> H["Route handler<br/>(the core)"]
  H --> M3R["Rate limit<br/>(response path)"]
  M3R --> M2R["Auth<br/>(timing, audit)"]
  M2R --> M1R["Headers stamped"]
  M1R --> RS["Response"]
  M2 -.->|"short-circuit:<br/>401, nothing inner runs"| RS
```

The half most explanations omit: the pipeline runs **both ways**. Django's docs draw it as an onion — each middleware a layer around the view at the core — and code *after* the `next()` call (or after `get_response`) runs on the response's way back out, in reverse order. That's where response timing gets measured, headers get stamped, and logging records what actually happened. A layer that short-circuits doesn't just skip the handler; it skips every inner layer's both halves — which is precisely the guarantee an auth gate exists to give.

## Order bugs that ship

The generic advice is "order matters"; the specific bugs are more instructive. **Authorization before authentication:** the permission check runs against an anonymous principal — intermittent 401s/403s, no exception anywhere, hours of debugging. **Body parser after routes:** every handler sees `req.body === undefined` and blames the client. **Auth before CORS:** the browser blocks the 401 response itself for lacking CORS headers, so the frontend sees a network error instead of the real one. **Static files before auth:** private files served cheerfully to the unauthenticated. **Error handler not last:** errors thrown after its position in the stack never reach it. Every one of these passes a smoke test on the happy path — order bugs are the kind that ship.

## Short-circuiting: when *not* calling next() is the point

The contract has two legal exits: pass control on, or end the cycle. Ending it early is not a failure of middleware — it's half its job: the 401 from the auth gate, the 429 from the limiter, the cache hit, the redirect, the CORS preflight answered on the spot. The rule that keeps both exits honest: *always do exactly one* — respond, or `next()`. Doing neither hangs the request forever; doing both throws headers-already-sent errors that confuse everyone downstream.

## The same idea in every framework

| Framework | The middleware is | Pass control | On the way out |
| --- | --- | --- | --- |
| [Express](https://expressjs.com/en/guide/using-middleware.html) | `(req, res, next) => {}` | `next()` | Code after `next()` (with care) |
| [Django](https://docs.djangoproject.com/en/5.2/topics/http/middleware/) | Callable wrapping `get_response` | `get_response(request)` | Code after the call — the onion |
| [Rack / Rails](https://guides.rubyonrails.org/rails_on_rack.html) | Object with `call(env)` | `@app.call(env)` | After the call returns |
| Koa / Hono | `async (ctx, next) => {}` | `await next()` | After the `await` — first-class |

One model, four accents. The error path gets its own convention per framework — Express's four-argument `(err, req, res, next)` signature *is* the registration mechanism, which is why deleting an "unused" parameter silently turns the error handler into a normal middleware that never fires.

## Middleware vs. gateways vs. hooks

| | Middleware | [API gateway](/glossary/api-gateway-architecture/) | [Data hooks](/glossary/database-triggers-beforesave-aftersave/) |
| --- | --- | --- | --- |
| Runs | Inside one app's process | In front of many apps | Around data operations |
| Granularity | Per request | Per request, cross-service | Per save/delete/find |
| Owns | This app's pipeline | Routing, edge auth, global limits | Validation, data reactions |
| Configured by | Code, in order | Infrastructure config | Per-class registration |

Three interception layers, one nesting: the gateway fronts the fleet, middleware runs each app's gauntlet, and hooks fire where requests become data. A concern belongs at the outermost layer that can decide it — global rate limits at the gateway, session auth in middleware, "is this write valid" at the hook.

## Common use cases

- **Authentication and session handling** — establish identity once, early, for everything after.
- **Cross-cutting hygiene** — [CORS](/glossary/cors-cross-origin-resource-sharing/), security headers, compression, request IDs.
- **Input discipline** — body parsing with size limits, content-type enforcement, validation.
- **Observability** — logging and timing wrapped around the whole pipeline via the onion's return path.
- **Traffic protection** — [rate limits](/glossary/api-rate-limiting-throttling/) and abuse gates that short-circuit before cost is incurred.

## Which layer does this belong in? A decision matrix

| Concern | Layer |
| --- | --- |
| Applies to every app you run | [Gateway](/glossary/api-gateway-architecture/) |
| Applies to every request of this app | Middleware, positioned deliberately |
| Applies to specific routes | Router-level middleware |
| Applies when data is written or read | [beforeSave / beforeFind hooks](/glossary/database-triggers-beforesave-aftersave/) |
| Custom business operations | [Functions](/glossary/cloud-code-serverless-functions/), not pipeline hacks |
| Error shaping | Error middleware — last, four args, no exceptions |

## Limitations and trade-offs

- **Order is invisible until it isn't.** The stack reads top-to-bottom but fails in ways that point everywhere else; treat middleware registration as reviewed, load-bearing code.
- **Every layer taxes every request.** Ten middleware at 2 ms each is 20 ms on every response; measure the stack like you measure queries.
- **Global state is a trap.** Middleware runs concurrently across requests; anything shared mutable becomes a race — attach per-request data to the request object, nowhere else.
- **Pipelines hide control flow.** A short-circuiting layer three deep can be why a route "never runs"; the debugging move is always: print the stack, in order.
- **Not everything is a pipeline concern.** Business logic smuggled into middleware couples every route to it; the pipeline is for *crossing* concerns, not central ones.

## Middleware on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. The relationship is unusually literal: **Back4app's server is itself Express middleware** — the JavaScript tab shows it mounted with `app.use('/parse', …)` into a standard stack — and the platform runs the canonical gauntlet for every request: security headers, CORS, bounded parsing, key checks, [session authentication](/glossary/session-management/), and rate limits, in the right order, maintained as infrastructure. Your custom per-request logic then goes where the decision-matrix points instead of into hand-rolled pipeline code: [`beforeSave`/`beforeFind` triggers](/glossary/database-triggers-beforesave-aftersave/) for data-adjacent rules, [Cloud Functions](/glossary/cloud-code-serverless-functions/) for operations — each with the request's user context attached, which is most of what custom middleware ever wanted to know.
