Engineering Notes
Home/Backend/Frameworks/Routing (Backend Frameworks)
Backend · Frameworks

Routing (Backend Frameworks)

Routing maps a method and path to a handler. Learn static, parameter and wildcard segments, why registration order bites in Express, and how to group routes under prefixes with shared middleware.

⚙️ Backend● Beginner⏱ 4 min read▶ 1 interactive🛠️ 3 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • What routing means on the backend
  • Static, parameterized and wildcard segments
  • Specificity and ordering
  • Grouping/prefixing, and contrast with frontend routing

📚 Before you start

1What "Routing" Means at the Backend Layer

Routing is the mechanism that maps an incoming request's method + path to the specific function that should handle it. 01-express_fundamentals/ and 02-nestjs_fundamentals/ each showed one concrete implementation of this (imperative app.get() calls vs. decorator metadata read at bootstrap) — this topic zooms out to the routing mechanics themselves, independent of any one framework's syntax: how patterns are matched, how specificity/ordering is resolved, and how routes are organized at scale.

2Static, Parameterized, and Wildcard Segments

text
/health                    -- static: matches exactly one literal path
/users/:id                 -- parameterized: :id matches any single segment, captured as a value
/files/*                   -- wildcard: * matches one-or-more remaining segments, captured as one string
/users/:id/orders/:orderId -- multiple params in one pattern

A router's matching engine walks a request path segment-by-segment against each registered pattern's segments: a static segment must match the literal text exactly, a parameterized segment (:name) matches any single segment and captures its value, and a wildcard consumes everything remaining. This is exactly the logic the Express exercises in 01-express_fundamentals/ implement by hand as matchRoute — real routers (Express's, NestJS's underlying Express/Fastify, Next.js's file-based router) all do a more optimized version of this same fundamental segment-matching idea.

3Specificity and Ordering

text
GET /users/me      <- registered FIRST
GET /users/:id      <- registered SECOND

When two patterns could both match the same incoming path (/users/me matches the literal pattern AND the :id pattern, since "me" is a valid value for :id), most routers resolve the ambiguity by registration order: whichever pattern was declared first wins, checked in the order routes were added. This is why route-ordering discipline (most specific / most literal first) is a real, load-bearing convention, not a stylistic preference — get it backwards and the more specific route silently never runs, exactly as covered in 01-express_fundamentals/ Common Pitfall 1.

Some routers (Next.js's file-based router among them) instead resolve ambiguity by an explicit specificity ranking baked into the routing algorithm itself — a static segment always outranks a dynamic segment at the same position, regardless of file/declaration order — which removes the "order matters" footgun entirely, at the cost of the ranking rules themselves needing to be learned.

Routes form a tree of segments where static beats parameter beats wildcard, except in Express which matches in registration order
Routes form a tree of segments where static beats parameter beats wildcard, except in Express which matches in registration order

4Route Grouping and Prefixing

ts
// Express: a whole sub-router mounted under one prefix
const usersRouter = express.Router();
usersRouter.get("/", listUsers);       // becomes GET /api/v1/users
usersRouter.get("/:id", getUser);      // becomes GET /api/v1/users/:id
app.use("/api/v1/users", usersRouter);

// NestJS: @Controller("users") on a class mounted inside a module that's
// itself imported under a global prefix (app.setGlobalPrefix("api/v1"))

Grouping related routes under a shared prefix (an API version, a resource name, an admin-only section) keeps route declarations organized as the number of endpoints grows, and centralizes prefix changes (e.g. bumping an API version) to one place instead of editing every individual route string.

5Contrast with Frontend Routing

Both this topic and frontend/ecosystem/06-react_router/ (and Next.js's own page router) solve a "match a path to a handler" problem, but the handler on each side does a fundamentally different job: a backend route handler processes a request and returns data or a status — no rendering is implied. A frontend route handler decides which UI component tree to render for a given URL, entirely client-side (React Router) or via server-rendering (Next.js pages). The matching mechanics (static/parameterized/wildcard segments, specificity resolution) are conceptually the same problem in both places — it's the handler's job on the other end that differs.

⚠️ Common Pitfalls

  1. Registering a general pattern before a more specific literal one, in a router that resolves ambiguity by registration order — the specific route silently never runs (section 3).
  2. Deeply nested manual route strings repeated across files instead of grouping related routes under one mounted prefix — makes a prefix change (like an API version bump) require hunting down every individual route string.
  3. Assuming wildcard and parameterized segments behave the same way. A wildcard (*) swallows multiple remaining segments as one value; a parameterized segment (:id) matches exactly one segment — mixing them up produces routes that either over-match or under-match.
  4. Forgetting that route matching is case-sensitive and trailing-slash-sensitive by default in most routers — /Users and /users, or /users and /users/, are not automatically treated as the same route unless the router is explicitly configured to normalize them.

📋 Quick Reference

text
/health              -- static segment, exact literal match
/users/:id            -- parameterized segment, matches one segment, captured
/files/*               -- wildcard, matches one-or-more remaining segments

Ambiguity resolution:
  registration-order routers (Express, NestJS's underlying router) -- first match wins
  ranking-based routers (Next.js file router) -- static always outranks dynamic, order-independent

Group related routes under one mounted prefix -- centralizes prefix changes (versioning, resource grouping)
Backend routing -> returns data/status | Frontend routing -> decides which UI to render

🧠 Key takeaways

  • Routes match method + path pattern: static, :param, wildcard, constrained params.
  • Express matches in registration order: put static routes before parameter routes.
  • Group with routers and prefixes (/api/v1) and attach shared middleware once.
  • Backend routing selects a handler per request; frontend routing swaps views in the browser.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1How do routers match requests?core▾

By HTTP method and path pattern, often with parameters (/users/:id), wildcards and optional segments. Implementation varies: Express tests routes in order; Fastify and others use a radix tree for fast lookups.

Follow-ups interviewers ask
  • Why does order matter in Express? /users/me must be declared before /users/:id.
Q2How do you organize routes in a large app?core▾

Group by feature/resource with modular routers, mount them under prefixes (/api/v1/orders), keep handlers thin, and apply middleware (auth, validation) per router or route.

Follow-ups interviewers ask
  • Attribute routing? .NET/NestJS decorators on controller methods.
Q3Path params vs query params vs body?core▾

Path params identify a resource (/orders/42); query params filter, sort or paginate (?status=paid); the body carries data for create/update. Don't put sensitive data in URLs (they're logged).

Follow-ups interviewers ask
  • Where do optional filters go? Query params.
Q4How do you version routes?senior▾

Prefix (/v1, /v2) mounted to different routers or controllers, sharing services underneath where possible; or header-based selection with middleware.

Follow-ups interviewers ask
  • Risk of copy-pasted v2 controllers? Duplicated logic diverging over time.
Q5How do you handle 404 and 405 correctly?core▾

Return 404 when no route matches the path, and 405 Method Not Allowed (with an Allow header) when the path exists but the method doesn't. Many frameworks need explicit configuration for 405.

Follow-ups interviewers ask
  • Catch-all handler placement? After all routes.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. GET /users/me hits getUser(':id'). Why?
First match wins in Express.
2. Which matches /files/a/b/c.txt?
Wildcard spans segments.
3. Benefit of routers with prefixes?
Organization.
4. Frontend routing differs because…
Client-side navigation.