In a meta-framework, what does a per-request interception step run in front of, and what can it return?
answer
- runs before the framework picks a route
- envelope only: URL, headers, cookies
- no route parameters, no loaded data
- continue, rewrite, redirect, answer, set headers
- a matcher decides who pays for it
basics
~20 sA per-request interception step runs for every matching request before the framework resolves a route, renders it, or looks in its cache. It can continue, rewrite to another path, redirect, answer directly, or attach headers and cookies.
solid answer
~40 sIt is code the framework invokes for every request whose path a matcher selects, and it runs *before* route resolution, rendering and any lookup of an already-rendered copy. It therefore sees only the request envelope: method, URL, headers and cookies, plus whatever the hosting target attaches such as the client address. It has no route parameters, no loaded data and no rendered output, because none of that exists yet. Its outcomes are small and cheap: let the request continue, rewrite the path so a different route answers without changing the address, redirect, answer the request itself, or let it through while attaching response headers or a `Set-Cookie`. Anything that needs the data the route is about to load does not belong at this station.
go deeper
Remember the position and the outcome list: it runs for every matching request before routing and rendering, and it can continue, rewrite, redirect, answer, or attach headers and cookies.
Be able to explain why the envelope is all it gets: routing has not run, so route parameters and loaded data do not exist yet, which bounds what the step may legitimately decide.
Show that you treat it as a separate deployment unit with its own runtime and cold path, and that you know every matched request pays an invocation even when the step just continues.
Frame it as a budget: one station that all matched traffic crosses. Argue about what earns a place there against what belongs in the route or in a rule the hosting target applies before your code.
## Where the step sits Most meta-frameworks move a request through a fixed sequence of stations: the hosting target receives it, your own code gets one chance to look at it, the framework resolves **which route module owns the path**, it decides whether an already-rendered copy of that route can answer, and only then does it render and return HTML or data. **Per-request interception** is the code you place at the second station — a module the framework invokes for every request whose path a **matcher** selects, before route resolution, before rendering, and before the framework consults whatever cache of prerendered or regenerated output it keeps. That position is both the point and the price. It is the only place in your own code that sees a request which has not yet been attributed to a route, which is what lets one file redirect, rewrite or gate an entire section of the site uniformly. It is also placed in front of requests that would otherwise have been answered without running any of your code at all. ## What it receives - the request **method** and the full **URL**, path and query string exactly as sent - the **request headers**, including `Cookie`, `Accept`, `Accept-Language` and any forwarding headers the hosting target adds - whatever the hosting target chooses to attach — commonly the client address and a coarse geography hint - **nothing produced by routing**: no matched route module, no parsed route parameters, no loaded data, no response body The boundary matters more than the list. The step can read the raw path string and split it itself, but the framework has not yet decided which route owns that path, so there is no authoritative notion of "the product id in this URL". Any rule whose real input is the record the route is about to load, the viewer's stored role, or the markup about to be produced cannot be decided here without fetching it — and fetching is the one thing this position cannot afford on every request. ## What it can produce | Outcome | What the framework does next | |---|---| | Continue | Routing, cache lookup and rendering proceed as if the step were absent | | Continue with response mutations | The same, plus response headers or a `Set-Cookie` attached on the way out | | Rewrite | A different path is resolved internally; the address the client sees does not change | | Redirect | A response carrying a `Location` header goes back; no route module runs | | Answer directly | The step returns a response itself and nothing downstream runs | ## What it must not do 1. **Render.** Producing markup is the route module's job; doing it here duplicates the framework's own work and blocks the fast path for everything else the matcher selected. 2. **Read or parse a large request body.** The step is meant to look at the envelope; buffering an upload here adds its size to the latency of a station every matched request passes through. 3. **Call a slow dependency per request.** A database round trip or a remote permission lookup at this station adds its latency to every matched request, including ones that would otherwise have been served from a cached copy. 4. **Hold state in memory across requests.** This code is typically replicated across many short-lived instances, so anything remembered in a local variable is visible to an arbitrary subset of traffic and lost without notice. ## Why the deployment shape matters The step is usually packaged as **its own deployment unit** with its own runtime, its own limits on execution time and memory, and its own cold path. Two consequences follow. First, sharing helpers with a route module is not free — the shared code is bundled into both units, and a dependency that works inside a route may be unavailable in the restricted runtime the step is given. Second, a request that the matcher selects pays an invocation even if the step immediately decides to continue, whereas a path the matcher excludes costs nothing at all because the unit is never woken. ## Where meta-frameworks differ Meta-frameworks do not agree on the shape of this station. Some expose exactly one module that runs for everything the matcher selects; others let the same kind of step be attached per route segment or per layout as well, so a request may pass through several before routing finishes. Some run it in a restricted runtime by default and others in the same process as the routes. What is common across all of them is the position: ahead of routing, rendering and the cache, with only the request envelope in hand and a short list of cheap outcomes to choose from.
- If the step can read the raw path, why is that not the same as having route parameters?Route parameters are the output of route resolution: the framework compares the path against the route table and binds segments to names. The step runs before that, so it can slice the path string itself, but nothing has confirmed that the path matches a route at all, or which one, or how its dynamic segments are named.
- What is the practical difference between rewriting and redirecting from this step?A rewrite resolves a different path internally while the address in the client's bar stays the same, so it is a server-side substitution. A redirect sends a response back and the client makes a second request to the new address, so it is visible, bookmarkable and costs an extra round trip.
- Why can this step attach response headers when it never sees the rendered body?It wraps the downstream work: it hands the request onward and gets the outgoing response handle back, so it can add or overwrite header fields and set cookies without inspecting or rewriting the payload. That is why constant security headers are cheap to apply here and body-dependent decisions are not.
It is the doorman in the lobby, not the receptionist on the floor: he sees who walks in and what they carry, can wave them through, send them to another entrance or turn them away, but he has not yet looked up which office they are visiting.
saying these in an interview costs you the question
- Thinks it can read the route parameters the framework parsed
- Renders part of the page there to save the route work
- Assumes it sees the response body it is adding headers to
- Believes it runs only for page requests, never for assets
- Keeps a cache in a module-level variable and trusts every request to see it