Stage 2: The Router
stage 2 of 4 · ~25 min · runs in your browser
Objective:Build the pattern-matching engine at the heart of every web framework's routing layer — the thing that maps `GET /users/:id` to a handler.
Every framework's app.get("/users/:id", handler) is a tiny pattern
matcher. Build it.
Implement createRouter() returning:
add(method, pattern, handler)— register a route; pattern segments starting with:are parameters (/users/:id/posts/:postId)handle(method, path)— find the matching route and returnhandler({ params }); return{ status: 404 }if no pattern matches, and{ status: 405 }if a pattern matches but with the wrong method (real servers distinguish "no such resource" from "wrong verb").
Matching rules:
- Exact segment count required
- Literal segments must equal exactly
:namesegments capture whatever is in that position- First registered match wins (order matters)
Why 404 vs 405 matters: If GET /users/1 exists but a client sends
DELETE /users/1, a 404 says "this resource doesn't exist" — which is
wrong. A 405 says "the resource exists but that verb is not allowed" — which
is correct and lets the client know to change their approach.
Why this matters in production
Express's `app.get("/users/:id", handler)`, Fastify's route registration, Next.js's file-based router — they all reduce to a function that matches a method + path against registered patterns and extracts named parameters. Understanding how this works explains why `:id` in Express and `[id]` in Next.js are the same concept with different syntax.
tests (6)
○ routes a literal path
○ captures path parameters
○ unknown path is 404
○ wrong method is 405 not 404
○ first registered match wins
○ root path matches