What happens to the rest of a middleware chain when a hook answers a request itself instead of delegating onward?
answer
- delegation is optional, not automatic
- no call means nothing deeper runs
- skipping is scoped inward only
- enclosing after-phases still resume
basics
~20 sThe chain ends there: every hook nested inside and the route handler are skipped entirely. The hooks already entered around it still resume normally, so their after-phases run and can log, time or decorate that early response.
solid answer
~50 sA hook short-circuits by producing a complete response — status, headers, body — and returning **without** calling the rest of the chain. Because each hook only reaches deeper by delegating, not delegating means nothing deeper exists for this request: inner hooks never run at all, in either phase, and the route handler is never invoked. What does still run is everything already entered around it. The outer hooks are suspended inside their own delegation call; that call simply returns earlier than usual, so their after-phases execute exactly as they would for a normal response. This is the mechanism behind rejecting an unauthenticated caller, answering a throttled one with 429, serving a cache hit, or issuing a redirect. The one thing you must not do is respond and then delegate anyway — that leaves two attempts to write one response, which most frameworks handle badly.
go deeper
Know that a hook can answer a request on its own by not passing it along, and that when it does, the route handler is never called. Rejecting an unauthorised caller before any real work happens is the everyday example.
Explain the scope precisely: inner hooks and the handler are skipped in both phases, while hooks already entered around the decision resume as normal. Mention that responding and then delegating anyway is the classic double-response bug.
Show you reason about what the early response is missing — headers, encodings or identifiers contributed by hooks inside the short circuit — and that you place observability outside the decision so rejected traffic still appears in logs and metrics.
Talk about who is allowed to end a request early. Short-circuit points are effectively an API for the whole service, and an undisciplined set of them makes the real behaviour of an endpoint impossible to read from the endpoint itself.
## What short-circuiting is Every hook in a chain holds the remainder of that chain as something it may call. Delegating is therefore a choice, not an obligation. **Short-circuiting** is the case where a hook makes a decision, builds a response itself, and returns without ever making that call. It is an ordinary return, not an error: no exception is involved and nothing exceptional has to be signalled. Typical reasons to do it: the caller is not authenticated or not permitted; the caller has exceeded a quota and gets 429; a cached representation is still fresh and can be served without touching the handler; the request should be redirected; a conditional request can be answered with 304. In each case the work further inside the chain is not merely unnecessary — it may be actively undesirable, which is the whole point of putting the decision outside it. ## What is skipped and what still runs | Element | Before-phase | After-phase | |---|---|---| | hooks registered outside the short-circuiting hook | already ran | still runs | | the short-circuiting hook itself | ran, and produced the response | its own remaining code runs | | hooks registered inside it | never runs | never runs | | the route handler | never invoked | n/a | The crucial asymmetry is the last two rows against the first. Skipping is scoped to what sits **inside** the hook that stopped the chain; it says nothing about the layers enclosing it. Those layers cannot tell the difference structurally — from where they sit, a call returned with a response, which is what they expected in the first place. ## Why the outer after-phases still running matters This is a feature, not an accident, and it is what makes an outer position the right home for anything that must observe all traffic: - **Access logging and metrics stay complete.** Rejected and throttled requests are still recorded, with their real status, because the logging hook resumes normally. - **Total timing still works.** An outermost timer brackets the short-circuit exactly as it brackets a full request. - **Response-wide decoration still applies.** A hook that stamps a header onto every outgoing response continues to do so on the early response, as long as it is outside the short-circuit. The converse is the trap: anything that would have been contributed by a hook **inside** the short-circuit is simply absent from that response. If a header, an encoding step or a correlation value is attached by an inner hook, the short-circuited response goes out without it, and a test that only exercises the happy path will never notice. The fix is positional — move the contributor outward so it encloses the decision — rather than duplicating its logic into the short-circuiting hook. ## Writing one correctly 1. Decide as early as the information allows, but no earlier than the data you need to decide is available. 2. Build a **complete** response: status, the headers that response needs, and a body if it has one. Nothing downstream will fill in gaps. 3. Return without delegating — and make sure the control flow really does return, rather than falling through into the delegation call. 4. Check what you are skipping. If something inside the chain was contributing to every response, this response now lacks it. ## The classic failure: responding and delegating anyway Writing a response and then still calling the rest of the chain is the most common bug in this area. The chain runs on regardless — a response already existing does not by itself cancel the remaining pipeline in most frameworks — and the handler eventually tries to write a second response into the same exchange. Depending on the framework you then get the second write silently dropped, an error about the response already being committed, or two bodies concatenated on the wire. Symptoms are confusing precisely because the hook looks correct in isolation; the defect is in what happened after it. A related subtlety: short-circuiting from a hook that has already flushed part of the response is not really a short circuit at all. Once bytes are on the wire, the status and headers are fixed and an early decision can no longer replace them. ## What interviewers are checking They want to hear that delegation is explicit and optional, that skipping is scoped inward, that the enclosing after-phases still run, and that the candidate has thought about the second-order effect — which contributions the response now silently lacks. A candidate who answers only 'the chain stops' has described half the mechanism and none of the consequences.
- A short-circuited response is missing a header that every other response carries. What is the likely cause?The hook that adds the header sits inside the hook that short-circuited, so it never ran. Move the contributor outward so it encloses the decision point, rather than copying the header logic into the rejecting hook, which would leave you with two places to keep in sync.
- Does short-circuiting mean the enclosing hooks are skipped too?No. Those hooks already ran their before-phases on the way in, and they are suspended inside their own delegation call. That call just returns sooner, so their after-phases run normally and can log, time or decorate the early response exactly as for a full request.
- Your request metrics are derived from handler invocations. What does a short-circuiting rate limiter do to that data?It makes throttled traffic invisible, because the handler was never invoked. Counting at an outer hook's after-phase instead captures every response the server actually sent, including rejections, redirects and cache hits that never reached the handler.
saying these in an interview costs you the question
- Believes the route handler always runs regardless of what hooks do
- Says short-circuiting also cancels the enclosing hooks' after-phases
- Writes a response and then delegates onward anyway
- Assumes the framework cancels the chain once a response exists
- Forgets that headers added by inner hooks are missing on the early response