What is Middleware (Request Lifecycle)?

Last updated: July 2026

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

QuestionAnswer
The contractInspect/modify → then respond (short-circuit) or call next()
The shapeAn onion: requests pass down the stack, responses bubble back up
The lawRegistration order = execution order — most middleware bugs are order bugs
The canonical stackHeaders → CORS → parsing → logging → authn → authz → limits → routes → 404 → errors
vs. the gatewayMiddleware runs inside one app; a gateway fronts many

The stack, in order

// 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)

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 (permissions checked against nobody are permissions granted to anybody); rate limiting 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

Middleware onion with request descending and response ascendingA 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.

short-circuit:
401, nothing inner runs

Request

Headers / CORS

Auth

Rate limit

Route handler
(the core)

Rate limit
(response path)

Auth
(timing, audit)

Headers stamped

Response

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.

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

FrameworkThe middleware isPass controlOn the way out
Express(req, res, next) => {}next()Code after next() (with care)
DjangoCallable wrapping get_responseget_response(request)Code after the call — the onion
Rack / RailsObject with call(env)@app.call(env)After the call returns
Koa / Honoasync (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

MiddlewareAPI gatewayData hooks
RunsInside one app’s processIn front of many appsAround data operations
GranularityPer requestPer request, cross-servicePer save/delete/find
OwnsThis app’s pipelineRouting, edge auth, global limitsValidation, data reactions
Configured byCode, in orderInfrastructure configPer-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 hygieneCORS, 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 protectionrate limits and abuse gates that short-circuit before cost is incurred.

Which layer does this belong in? A decision matrix

ConcernLayer
Applies to every app you runGateway
Applies to every request of this appMiddleware, positioned deliberately
Applies to specific routesRouter-level middleware
Applies when data is written or readbeforeSave / beforeFind hooks
Custom business operationsFunctions, not pipeline hacks
Error shapingError 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, 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 for data-adjacent rules, Cloud Functions for operations — each with the request’s user context attached, which is most of what custom middleware ever wanted to know.

Frequently asked questions

What is middleware in simple terms?

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.

What are common middleware examples?

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.

How does the middleware chain work?

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.

Does middleware order matter?

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.

What is error-handling middleware?

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.

What is the difference between middleware and a route handler?

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.

What is the difference between middleware and an API gateway?

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.

Is middleware the same in every framework?

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.

What about the older "enterprise middleware" meaning?

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.

When should middleware NOT call next()?

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.

Related terms

Compare with

Further reading

Ready to build your backend?

Start your project on Back4app in minutes — database, auth, APIs, and Cloud Code included. No credit card required.

Written and reviewed by Back4app Engineering, Back4app Engineering · Published 2026-07-30