skip to content

What parts of the incoming request reach a route module's server-side data fetch, and what must it never trust about them?

level: middleimportance: should knowfreq 52%

answer

  1. the whole request is in hand
  2. path, query, method, headers
  3. privileged code, untrusted input
  4. identifier asked for is not permission granted

basics

~20 s

A route's server-side fetch receives the whole request: matched path segments, query string, method, URL and headers. All of it is attacker-controlled, so validate every value before it reaches a query, a path, a redirect or an authorisation decision.

solid answer

~50 s

The route's data function runs with the request in hand: the matched path segments, the query string, the method and full URL, and the headers the client sent. Because it runs only on the server, it is also the place in the route that may hold real privileges — a database connection, an internal service token — that must never appear in anything the browser downloads. Those two facts combine into the rule: privileged code is being driven by untrusted input. A path segment is whatever someone typed or forged, a query parameter can appear many times or not at all, and a header can be absent or spoofed. So parse and validate each value against an expected shape before use, and derive authorisation from a verified identity rather than from a value in the URL. Trusting a route parameter because your own link produced it is the classic failure here.

go deeper

for a junior

Recall what the fetch can see — path segments, query string, method, URL, headers — and that all of it comes from the caller, so it needs checking before use.

for a middle

Explain why this position is privileged, and turn that into concrete rules: validate shape, parameterise queries, authorise by verified identity, and return only the fields the route renders.

for a senior

Show the failure modes you have actually fixed: identifier tampering that reached another tenant's record, an unbounded page size, an over-fetched record leaking fields into the payload.

for a principal

Decide where validation and authorisation belong as a convention — at every route, or in a shared layer every route passes through — and how the team proves a new route did not skip it.

## What arrives A route module's server-side data function is invoked with the request that triggered it. In framework-neutral terms it can see: - **The matched path segments** — the dynamic parts of the URL the router extracted, such as an identifier or a slug. - **The query string** — free-form key/value pairs, any of which may be absent, repeated, or arbitrarily long. - **The method and the full URL**, including the origin and path the client asked for. - **The request headers**, including content negotiation headers, any forwarding headers a proxy added, and the `Cookie` header. - **The body**, when the route's data function is also serving a non-`GET` request; reads normally carry none. The session hiding inside that `Cookie` header — how it is read, verified, and where that read belongs in the chain — is its own subject. What matters here is that the raw request is available and that the code seeing it is privileged. ## Why this is the privileged position This function never runs in a browser. That is what allows it to hold things a browser must never see: a database connection string, an internal service token, a signing key. The same property makes it the natural place to enforce access rules, because the browser cannot skip it — the data simply does not exist in the response unless this code produced it. The uncomfortable corollary: **the most privileged code in the route is the code most directly exposed to user input.** Every value listed above is chosen by whoever made the request. There is no such thing as a value that 'can only have come from our own link' — anyone can type a URL, edit one, or send a request without a browser at all. ## The trust rules 1. **Validate shape and range before use.** An identifier that should be a positive integer, a slug that should match a known pattern, a page number that should be bounded. Reject what does not fit instead of passing it on. 2. **Never build a query, command or file path by concatenating request values.** Use parameterised queries and lookups by allow-list; a path assembled from a segment invites traversal outside the intended directory. 3. **Authorise by identity, not by the URL.** A record identifier in the path says which record was *asked for*, never that the requester may see it. Load the record, then check the verified caller against it. A URL that returns someone else's data because nobody checked is one of the most common real-world defects at this layer. 4. **Treat headers as claims.** Forwarding and client-hint headers are added by whatever is in front of you and can be forged unless a trusted proxy is known to overwrite them. 5. **Do not reflect input into a redirect target or into markup unescaped.** A destination taken from the query string must be validated against an allow-list of internal paths. 6. **Missing and repeated values are normal.** Query parameters have no arity guarantee; code that assumes a single string will misbehave when given two. | Input | What it tells you | What it does not tell you | |---|---|---| | Path segment | Which resource was requested | That it exists or that the caller may see it | | Query parameter | What the client asked to filter or page by | That the value is well-formed, singular, or in range | | Header | What the client (or a proxy) claimed | Anything verified, unless a trusted hop guarantees it | ## Failure modes to name in an interview - **Injection** via a value concatenated into a query or command. - **Broken object-level authorisation**: fetching by identifier and rendering the result without checking who is asking. - **Over-fetching and leaking**: loading the whole record server-side and serialising all of it into the response, including fields the UI never shows. What crosses the boundary is visible to the user, so trim it in the fetch. - **Unbounded work**: a page-size parameter taken at face value turning one request into a table scan. - **Open redirects**, where a destination parameter is followed without validation. ## The shape of a good answer Name the inputs, then immediately name the posture: everything here is untrusted, this code is privileged, therefore validate at the boundary, authorise against a verified identity, parameterise every query, and return only the fields the route actually renders. That sequence — inputs, privilege, consequences — is what an interviewer is listening for.

  • Why is fetching a record by a path identifier and rendering it not enough, even behind a login?
    Being signed in proves who the caller is, not that the record is theirs. An identifier in the URL is chosen by the requester, so incrementing it reaches other people's records. Load the record, then compare its owner or the caller's permissions against the verified identity, and return a not-found or forbidden outcome otherwise.
  • The fetch loads a full user record and the page shows only a display name. Why does that matter?
    Whatever the data function returns is serialised into the response, so every extra field travels to the browser and is readable even if no component renders it. Select the fields the route needs, or map the record to a view-shaped object before returning it.
  • Can a route's data function trust a header a proxy added, such as a forwarded client address?
    Only if a trusted hop is known to overwrite it on every request. Otherwise a client can send that header itself and the value is a claim, not a fact. Treat it as untrusted input, configure the trusted proxy explicitly, and never make an authorisation decision on an unverified header.

saying these in an interview costs you the question

  • Trusts a route parameter because the app's own links produce it
  • Builds a query string by concatenating a path segment
  • Treats being logged in as authorisation for any identifier
  • Returns whole records and relies on the UI to hide fields
  • Assumes a forwarding header cannot be forged by the client