Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Routing

Routing is how a server decides what to do with an incoming request. Every request has two key pieces of information:

  • the HTTP method
  • URL path
GET /api/users
───┬─── ────┬───
   │        └── WHERE (route path)
   └─────────── WHAT (HTTP method)
        │
        ▼
Router matches → runs the correct handler function

Static Routes

Static routes have a fixed, unchanging path. Every part of the URL is a literal string.

GET    /api/books      → return all books
POST   /api/books      → create a new book
GET    /api/users      → return all users

Dynamic Routes

Dynamic routes contain variable segments in the URL path, denoted by a colon(:). These variables let you target a specific resource by its identifier. The server extracts the dynamic value from the URL and uses it in its logic

GET /api/users/:id
GET /api/users/123     → id = "123"
GET /api/users/456     → id = "456"
GET /api/users/abc     → id = "abc"

Query Parameters

Query parameters are key-value pairs appended to the URL after a ?. They are used for optional, non-semantic data like filtering, sorting, and pagination. Multiple query params are separated by &. They are especially important for GET requests since GET has no request body

GET /api/books?page=2
GET /api/search?query=javascript&sort=newest
GET /api/products?category=shoes&minPrice=50&maxPrice=200

Nested Routes

Nested routes express hierarchical relationships between resources by embedding multiple identifiers in a single path

/api/users/123/posts/456
      │         │
      │         └── Post ID 456 belonging to user 123
      └── User ID 123

/api          → base path
/users        → static, "users" collection
/123          → dynamic, specific user (path param)
/posts        → static, "posts" collection under that user
/456          → dynamic, specific post (path param)

Route versioning

As APIs evolve, you often need to make breaking changes such as changing response shapes, renaming fields, removing endpoints. If you change an existing endpoint, every client using it breaks. Versioning solves this by running multiple versions of the API simultaneously

GET /api/v1/products   → old format, still running for existing clients
GET /api/v2/products   → new format, for new clients

Common versioning strategies beyond URL versioning include passing the version as a header API-Version: 2 or as a query param ?version=2, but URL versioning is the most common and explicit.

Catch-All Routes

A catch-all (wildcard) route is a fallback handler that matches any request that didn’t match any defined route. It prevents the server from returning a confusing raw error. For example the following code returns 404 on every route that is not registered before it.

app.all('*', (req, res) => {
  res.status(404).json({ error: 'Route not found' });
});

Working of a router

When a request comes in, the router iterates through registered routes in order and pattern-matches the method + path:

Incoming: GET /api/users/123/posts

Registered routes:
  GET  /api/users           → no
  POST /api/users           → no (wrong method)
  GET  /api/users/:id       → no (path too long)
  GET  /api/users/:id/posts → yes MATCH → run handler

The router extracts dynamic segments, populates req.params, and calls the handler. If nothing matches, the catch-all fires.