Routing is the process of matching an incoming request's method and path to the correct handler function. This lesson shows how routing works underneath frameworks by building a small router with plain Node.js.
What Is Routing?
Routing maps combinations of HTTP method (GET, POST, ...) and URL path (/users, /users/42, ...) to the specific function that should handle that request. Frameworks like Express provide a declarative API for this (app.get('/users', handler)), but at its core, routing is just conditional logic on req.method and req.url.
As soon as routes need dynamic segments, like a user ID in /users/42, simple equality checks stop being enough, you need pattern matching, which is exactly what dedicated routing libraries and frameworks provide.
Using new URL() correctly separates the path from any query string, so req.url like /search?q=node still matches /search in the routes table.
Matching Dynamic Route Segments
// Matches /users/42, captures "42"
const match = pathname.match(/^\/users\/(\w+)$/);
if (match) {
const userId = match[1];
}
Static routes (/health) can be matched with simple equality or a lookup table.
Dynamic routes (/users/:id) need pattern matching, typically a regular expression.
Frameworks convert friendly patterns like /users/:id into regular expressions internally.
Route matching order matters when multiple patterns could match the same path.
Routing Concepts Cheatsheet
Core ideas behind any router, framework-provided or hand-rolled.
Concept
Example
Static route
GET /health
Dynamic segment
GET /users/:id
Query string
GET /search?q=node (parsed separately from the path)
Method + path key
"GET /users" as a lookup table key
404 fallback
Run when no route matches
Route parameters object
{ id: '42' } extracted from a dynamic segment
Building a Tiny Router Class
You can build a small reusable router that stores patterns and handlers, then matches an incoming request against each pattern in order, extracting any dynamic parameters along the way. This is essentially a simplified version of what Express does internally.
This simplified router only matches exact paths; a real implementation would convert patterns like /users/:id into regular expressions with named capture groups.
Why Route Order Matters
When both a specific route (/users/new) and a dynamic one (/users/:id) could match the same incoming path, the order routes are checked in determines which one wins. Registering the specific route before the dynamic one avoids the dynamic route accidentally "swallowing" it.
Common Mistakes
Matching req.url directly against a path without stripping the query string first.
Registering a dynamic route before a more specific static route that could be shadowed by it.
Hand-rolling complex regex-based routing instead of adopting a framework once routes multiply.
Forgetting to send a 404 response when no route matches, leaving the request hanging.
Key Takeaways
Routing matches an incoming request's method and path to the correct handler.
Static routes can use simple equality; dynamic routes need pattern matching.
Route registration order matters whenever multiple patterns could match the same request.
A missing-route fallback (404) is essential to avoid hanging requests.
Pro Tip
Even in a framework like Express, register more specific routes (/users/new) before more general dynamic ones (/users/:id) as a habit, it avoids an entire class of routing bugs regardless of which router implementation you're using.
You now understand routing at its core. Next, dig into the request and response objects themselves in detail.