skip to content

In a file-based router, how do dynamic, catch-all and optional segments differ in what they match and in what the route receives?

level: middleimportance: must knowfreq 78%

answer

  1. one segment, the rest, or neither
  2. dynamic is exactly one, non-empty
  3. catch-all hands back an ordered list
  4. optional adds the bare parent URL
  5. every value is still an untrusted string

basics

~20 s

A dynamic segment matches exactly one non-empty path segment and hands the route one value. A catch-all matches the whole remainder as an ordered collection. The optional form of either also matches the shorter URL where that part is absent.

solid answer

~40 s

Three shapes cover almost every variable URL. A **dynamic segment** stands for exactly one path segment: it matches one non-empty piece and hands the route module one value under the parameter name the folder or file declares. A **catch-all** stands for the rest of the path: it matches one or more remaining segments and hands them over in order, usually as a list. The **optional** variant of either also matches the URL where that part is missing, so one route can serve both the bare parent path and its deeper forms. The marker syntax differs between frameworks — brackets, a prefix character, a doubled marker for optional — but the three behaviours, and the fact that every matched value arrives as an untrusted string you still have to validate, are the same everywhere.

go deeper

for a junior

Recall the three shapes and what each matches: one segment, the rest of the path, or the shorter URL too. Remember that the value you receive is a string from the URL, never a checked identifier.

for a middle

Explain the mechanics: why a plain dynamic segment cannot match emptiness, what a catch-all hands the route, and which single URL separates a required catch-all from an optional one.

for a senior

Demonstrate defensive handling: validate lengths and contents at the route boundary, keep a broad catch-all from swallowing genuine not-founds, and pick a shape that makes wrong URLs fail at match time.

for a principal

Treat URL shape as a contract you will live with: fixed segments document and constrain, open catch-alls buy flexibility and defer every check to code, and the choice decides how much validation each team must repeat.

## Why variable segments exist A route table built from folders would be useless if every URL had to be spelled out on disk: a catalogue with fifty thousand products cannot have fifty thousand folders. So file-based routers reserve a **naming marker** that turns a folder or file name into a **parameter** rather than a literal. The marker differs by framework — square brackets, a leading symbol, a doubled marker for the optional form — but the vocabulary of shapes is shared. ## The three shapes | Shape | What it matches | What the route receives | Does it match the parent URL? | |---|---|---|---| | **Dynamic segment** | exactly one segment, non-empty | one value under the declared parameter name | no | | **Catch-all** | one or more remaining segments | the matched segments in order, usually a list | no | | **Optional dynamic** (where supported) | zero or one segment | the value, or an absent marker | yes | | **Optional catch-all** | zero or more remaining segments | a possibly empty list | yes | Two details in that table are the ones interviews probe. First, a **plain dynamic segment does not match emptiness**: a route whose only child is a dynamic segment leaves the parent's own URL unmatched, and the parent needs its own index route. Second, the difference between a required and an optional catch-all is exactly one URL — the bare parent — which is why a documentation section, where the root of the section is a real page, is the standard case for the optional form. ## Parameter names and depth Each dynamic segment declares a name, and several can stack down one path, so a route nested three levels deep can receive three distinct values. Two constraints follow: - **Names must be unique down one chain.** Two segments claiming the same parameter name on the same path shadow each other; routers either reject that or leave one value unreachable. - **Depth is fixed for dynamic segments and open for catch-alls.** Three stacked dynamic segments match exactly three-segment tails; a catch-all matches tails of any length, which is why a catch-all is the right shape for content whose depth is decided by the content itself. ## What arrives at the route, and what it is worth The values are **strings taken from the request path**. Routers normally hand them over percent-decoded, and a catch-all's value is usually the list of decoded segments — some routers hand the joined remainder instead, which matters the moment a segment legitimately contains an encoded separator. Beyond that, nothing has been checked: - An identifier segment is not a number until you parse it, and not a valid one until you validate it. - A catch-all's list length is attacker-controlled, so code that indexes it blindly can read past what it expects. - A catch-all tail is a path fragment from the outside world. Using it to build a filesystem path or a downstream URL without normalisation is the classic traversal mistake. Some routers can generate **types** for these parameters from the tree, so the route module knows a value's name and whether it is a string or a list. Types describe the shape the router guarantees; they do not make the contents trustworthy. ## Choosing a shape A short checklist covers most decisions: 1. **Is the number of variable pieces fixed?** If yes, use that many dynamic segments — the shape documents the URL and the match fails fast when the tail is the wrong length. 2. **Is the depth decided by the data?** Nested documentation, category trees and stored file paths want a catch-all. 3. **Is the bare parent path also a page?** Either give it its own index route, or use the optional form and branch inside the route on whether the parameter is present. 4. **Would the catch-all swallow real 404s?** A catch-all high in the tree matches nearly everything below it, so it must produce the not-found response itself for tails it cannot resolve, instead of rendering an empty page. ## The failure modes worth naming - **The missing index.** Everything under the parent works, the parent's own URL does not, and the folder looks complete because the dynamic child is right there. - **The wrong optionality.** A required catch-all quietly loses the section root; an optional one silently accepts the parent path into a route written as if a value always exists. - **The unvalidated tail.** A route reads the first element of a catch-all list without checking the length, and a two-segment URL becomes a runtime error instead of a not-found. - **The over-broad catch-all.** Placed high enough, it claims paths intended for routes nobody has written yet, turning future 404s into confusing empty pages.

  • Why does a route whose only child is a dynamic segment leave the parent URL unmatched?
    Because a dynamic segment stands for one non-empty path segment, and the parent URL has nothing in that position. The match simply fails, so the parent needs its own index route — or the optional form of the segment, which is defined to match the missing case.
  • When would you prefer stacked dynamic segments over one catch-all?
    When the URL's shape is fixed and meaningful: three stacked segments say the path is exactly year, month and slug, name each value, and reject a wrong-length tail at match time. A catch-all accepts any depth and pushes both the naming and the length check into your code.
  • What must a route do with a catch-all tail before using it?
    Treat it as untrusted input. Check the length before indexing, validate each segment against what you expect, and never concatenate the tail into a filesystem path or a downstream URL without normalising it — the tail is caller-controlled and can contain traversal sequences.

saying these in an interview costs you the question

  • Thinks a dynamic segment also matches the empty parent path
  • Believes a catch-all yields one joined string everywhere
  • Treats matched parameter values as already validated
  • Assumes optional and required catch-alls serve the same URLs
  • Indexes a catch-all list without checking its length
  • Uses several dynamic segments where the depth is open-ended