skip to content

Hook Attachment Points

Cross-cutting code attaches at the server edge, in the framework pipeline, or around the resolved handler, each seeing more. Interviewers ask where a check can run and what it cannot yet see.

on this pageshow

questions

4

In a web framework, what can a hook that runs before routing see, and what can it not yet know?

level: juniorimportance: must knowfreq 66%

answer

  1. runs while the request is still a message
  2. connection facts, raw bytes, no match yet
  3. path string, not route template
  4. no handler, no arguments, no return value
  5. outbound it sees the final bytes

basics

~20 s

A hook running before routing sees the raw message and the connection: method, request-target string, headers, client address, an unread body. The route template, path variables, handler, bound arguments and return value do not exist yet.

solid answer

~40 s

At that altitude the request is still just a message. The hook can read the method, the request-target exactly as sent (including query string and any percent-encoding), the headers, cookies as text, and connection facts such as the client address and whether the transport is encrypted. The body is available only as an unread stream. What does not exist yet is anything the router produces: the matched route template, path variables, the handler that will run, its declared metadata, its bound arguments, and its return value. A practical consequence is that any decision here is string matching on a path, not routing, and reading the body consumes the stream unless the framework buffers it or you put a replayable stream back.

go deeper

for a junior

Remember the simple rule: before routing there is a message and a connection, nothing more. Method, path string, headers, client address, unread body — yes. Route, handler, arguments — not yet.

for a middle

Be able to explain why: the router has not run, so nothing it produces exists. Mention the outbound asymmetry, that this altitude sees the final bytes and status of whatever response left.

for a senior

Show the operational consequences you have hit: path-string checks disagreeing with routing after normalization, unbounded metric cardinality with no template, and body consumption breaking downstream reads.

for a principal

Frame it as a visibility contract rather than a place in a list. Argue when a concern belongs at an altitude that sees every request including the refused ones, and what that costs in precision.

## What "before routing" means A server-side web framework accepts a connection, reads an HTTP request off it, and only then decides which piece of application code should answer. A hook attached **before** that decision runs while the request is still nothing more than a message plus a socket. The router has not run, so the concepts the router produces — a matched **route template**, **path variables**, a **handler** — do not exist yet. This is the outermost of the three altitudes at which cross-cutting code attaches, and its defining property is that it is *close to the wire and far from the application*. ## What such a hook can observe - **The request line** — the method token and the request-target string *exactly as the client sent it*, including the query string, a trailing slash, and any percent-encoding. It is a string, not a parsed route. - **Headers and cookies**, as text. `Host`, `Content-Type`, `Content-Length`, `Authorization` and the cookie header are all readable, but no header has yet been interpreted on the application's behalf. - **Connection facts the message itself does not carry** — the peer address and port, whether the transport is encrypted, and the negotiated protocol version. This altitude sits closest to the connection; deeper hooks see these only as far as the framework surfaces them on its request abstraction. - **The body as an unread stream** — bytes, with no type and no validation applied. - **Every request the server accepted**, including ones the framework will go on to refuse because nothing matched or the method was wrong. - On the way out, **the final status code and the serialized response bytes**, whatever produced them — a handler, a framework error page, or a redirect issued by an inner hook. ## What it cannot know yet | Fact | Visible before routing? | Why | |---|---|---| | Method, raw path string, headers | Yes | Parsed straight from the message | | Client address, transport details | Yes | Comes from the connection, not the message | | Body bytes | Yes, as an unread stream | Nothing has consumed or typed it | | Matched route template | No | Routing has not run | | Path variables | No | They are extracted *by* the match | | Handler identity and its metadata | No | No handler has been selected | | Bound, converted arguments | No | Binding happens after dispatch begins | | Handler return value or thrown exception | No | The handler has not been invoked | | Final serialized response | Yes, on the way out | This hook wraps the whole exchange | The asymmetry is worth stating plainly: on the **inbound** trip this altitude knows the least of any hook, and on the **outbound** trip it knows the most about what actually left the process, because it sees bytes rather than an object someone still intends to serialize. ## Why the distinction bites in practice 1. **A path string is not a route.** A check written as "does the path start with a given prefix" is doing text comparison. Case, a trailing slash, a duplicated separator or an encoded character can make that comparison disagree with the match the router later performs on the same request. 2. **Metrics labelled here have unbounded cardinality.** With no template to group by, every distinct identifier in a path becomes its own label value. 3. **Reading the body has a cost.** A request body is typically a forward-only stream that can be consumed once. A hook that reads it to log or inspect it must buffer it and make a replayable stream available, or the code that runs later finds an empty body. 4. **It still runs for requests the application never handles.** That is a feature when you want a complete record of what the server received, and a trap when you assume a record at this altitude implies application code ran. ## Frameworks differ here Frameworks place the boundary differently: in some, route matching happens *inside* the chain of hooks, so the elements registered before the matching step are blind to the route while the ones after it are not; in others matching happens first and every chain element can see the matched route. Some additionally expose an attachment point *outside* the framework altogether, at the server or container that hands the request in. The mechanism to remember is not where any one product draws the line but the rule that produces it: **a hook can only see what has already been computed when it runs.** Ask, for any attachment point, "has routing happened yet, and has the handler been invoked yet?" — the answers tell you exactly what is on the table.

  • If a pre-routing hook must inspect the request body, what does it have to do so the rest of the request still works?
    Buffer what it reads and make a replayable stream available to whatever runs next. A request body is normally forward-only and consumable once, so an inspecting hook either wraps the stream with a recording version or reads into memory and substitutes a fresh stream. It should also bound the buffer, because an unbounded read turns a large upload into a memory problem.
  • Why can a pre-routing hook still produce a complete access record when routing fails?
    Because it wraps the whole exchange rather than a handler invocation. Requests that match nothing, use an unsupported method, or are rejected during parsing still pass through it inbound and still have a response leave through it outbound, so it observes the status the client actually received even when no application code ran.
  • How can a decision made on the raw path at this altitude disagree with the framework's own routing?
    The hook compares text; the router applies its matching rules. Normalization differences — case, a trailing slash, repeated separators, percent-encoded characters — can make a textual prefix test and the later match reach different conclusions about the same request, which is how a check meant to cover a path ends up not covering it.

saying these in an interview costs you the question

  • Assumes a pre-routing hook already knows the matched route template
  • Thinks path variables are available before routing has run
  • Reads the request body there and expects the handler to still read it
  • Believes requests that match no route skip this altitude entirely
  • Treats a raw path string and a route template as the same thing
  • Expects to see the handler's return value on the way out
open as a page

How do the server-edge, pipeline, and handler-aware hook altitudes in a web framework differ in what they can observe?

level: middleimportance: must knowfreq 72%

basics

~20 s

Each altitude sees more application meaning and less of the wire: the server edge sees connection facts and raw bytes, a pipeline hook the framework's request and response objects plus the route, a handler-aware hook the bound arguments and return value.

open as a page

Why do hooks that wrap a resolved handler never observe some requests the server actually received?

level: seniorimportance: should knowfreq 54%

basics

~20 s

A handler-aware hook runs only when a handler is invoked, so anything refused earlier — no route match, wrong method, unacceptable media type, a body that failed to convert, an outer credential check — is answered without ever reaching it.

open as a page

A policy needs a fact captured at the server edge and an operation identity that only routing produces — where should it decide?

level: principalimportance: should knowfreq 44%

basics

~20 s

The decision must run at or below the altitude of the latest fact it needs: capture the connection fact at the outermost hook into per-request state, then decide deeper, where the operation identity exists. Facts travel inward, never outward.

open as a page