skip to content

Why does a matched route parameter arrive as text, and what must a route entry do before a screen uses it?

level: middleimportance: must knowfreq 70%

answer

  1. matching proves shape, not value
  2. the address bar is an input field
  3. convert at the boundary, once
  4. parse into typed data, do not merely check
  5. failure is a routing outcome

basics

~20 s

A URL is text and a pattern checks only path shape, so every captured value is untrusted input. The route entry converts it into typed data once, at that boundary, and takes a failure branch when conversion fails.

solid answer

~50 s

Matching proves the path had the right *shape*, nothing about the value. The user can type, edit, bookmark and share a URL, so a captured segment is **input**, not an argument the app passed itself. The fix is a parse step that belongs to the route entry: take the raw parameters, decode is already done for you by the router, then validate and convert — this identifier becomes a number or a known identity, that search value becomes one of a fixed set of sorting choices — and produce either a typed value or a failure. Doing it once at the entry means every component below receives the typed value and none of them re-parse or re-guard. When conversion fails, that is a routing outcome: a nonsense identity goes to the not-found entry, while an unusable filter value is better treated as absent than as a crash.

go deeper

for a junior

Remember that anything read out of a URL is text a person could have typed. Convert it where the route is defined, and know what the screen should show when the conversion fails.

for a middle

Explain parse-over-check: the entry returns a typed value or a failure, so no component downstream re-guards. Be able to place the boundary relative to the data load and justify it.

for a senior

Demonstrate the taxonomy of failures — a claimed identity goes to not found, a decorative value degrades to a default — and show how the parse step is unit-tested against hostile URLs.

for a principal

Argue the boundary as policy: one convention for how every route turns URL text into typed input keeps stale links working across releases and keeps error handling out of feature code.

## The URL is input, not an argument When a screen is opened by an in-app link, the parameters were produced by the app itself and are usually well formed. That is the lucky path, and it hides the problem. The URL is also the one input a user can author by hand: typed, edited in the address bar, pasted from a chat, restored from a bookmark made six months ago against an older version of the app. A route pattern narrows what can arrive — a one-segment placeholder rejects a too-deep path — but it says nothing about the **value**. `/orders/12abc`, `/orders/0`, `/orders/99999999999999999999` and `/orders/%20` all satisfy a pattern that expects one segment. So the honest model is: **matching yields text plus a promise about shape**. Everything else is your job. ## Parse, do not validate The useful discipline is to convert rather than to check. A check leaves the raw text in play and every later reader has to trust that the check happened. A conversion produces a **new, typed value** that cannot be malformed, and the raw text stops travelling: - an identifier becomes a number in range, or a branded identity type, never a bare piece of text; - a date segment becomes a real date, with the impossible ones rejected; - a sort or tab search value becomes one member of a closed set of allowed choices; - a page number becomes a positive integer with a ceiling, so a huge value cannot become a huge request; - a list-shaped search value becomes a list of validated members, with unknown members dropped. The parse step's return type is the screen's real input contract. If it returns a typed value, the screen has no invalid state to render; if it can fail, that failure is explicit and has exactly one handler. ## Where the boundary belongs 1. **At the route entry**, next to the pattern that captured the values. Same place, same file, same review. 2. **Before or with the data load**, so the loader receives typed values and cannot be handed junk to put into a request. 3. **Once**, not per component. A screen with five components that each convert the same identifier has five chances to disagree, and no single place to change the rule. A useful consequence: because the conversion is a plain function of the captured parameters, it is directly testable — feed it hostile values and assert the failure branch, with no rendering involved. ## The failure branch Failures are not all the same, and treating them identically is a common defect. A rough guide: | Value | Bad input | Sensible outcome | |---|---|---| | Identity in the path | not a number, not a known shape | the not-found entry | | Identity in the path | well formed, nothing exists | the not-found entry, from the load step | | Filter or sort in the query | not an allowed choice | treat as absent, render the default view | | Page number | zero, negative, absurd | clamp, or treat as absent | | Unknown extra search value | anything | ignore it; do not fail on values you do not own | The principle: when the URL **claims an identity** that cannot exist, say not found. When the URL merely **decorates a view**, degrade to something sane rather than punishing a user for a stale link. Either way, decide in the entry, not in the middle of rendering. ## What typed parameters buy downstream - Components stop defending themselves, so their code is about the screen, not about the URL. - The failure path is one branch in one place instead of scattered empty states. - Requests are built from typed values, so raw user text never reaches a request path or a query untouched — encode whatever you put back into a URL. - In a typed codebase the parse step is where an unsafe value becomes a safe one, which makes the boundary visible to the type checker rather than a matter of memory. ## Traps - **Assuming the pattern typed it.** Patterns describe paths, not ranges; nothing in a pattern makes a value a number. - **Letting the request layer be the validator.** A malformed value then becomes a failed request, and the user sees a generic error where they should see not found. - **Re-parsing per component.** Divergent rules and duplicated error states follow. - **Failing hard on a decorative value.** A stale bookmarked filter should not take the whole screen down. - **Interpolating raw captured text into a request URL.** Encode on the way out; a value that happens to contain a separator or an ampersand otherwise changes the request's meaning.

  • Where would you put the conversion so both the data load and the screen see the same typed value?
    In the route entry itself, running before or as part of its load step, with the typed result passed down. The loader then cannot be handed a malformed value, and the screen receives the same object the loader used, so there is one rule and one failure branch instead of a guard in every component.
  • Should a value the app does not recognise in the query string ever fail the route?
    Usually not. Unknown extra values may come from tracking, other tools or a future version, so ignoring them keeps old links working. Fail only when a value the app does own is unusable and the screen cannot be rendered honestly without it — and prefer the default view to an error for anything merely decorative.
  • How do you test parameter handling without a browser?
    The parse step is a plain function from captured parameters to a typed result, so call it directly with hostile inputs: wrong shapes, out-of-range numbers, missing values, absurd lengths, unexpected choices. Assert the typed value in the good cases and the exact failure in the bad ones; only the rendering of the failure needs a rendered test.

saying these in an interview costs you the question

  • Trusts a captured value because the pattern matched it
  • Converts the identifier again in every component that reads it
  • Lets the request layer be the only validator, so bad input becomes a generic error
  • Interpolates raw captured text into a request path without encoding
  • Fails the whole screen for an unrecognised decorative search value
  • Thinks a route pattern can constrain a value to numbers