> For the complete documentation index, see [llms.txt](https://naz.gitbook.io/my-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://naz.gitbook.io/my-docs/backend-learning/routing_in_backend.md).

# Lesson 2: What is Routing in Backend?

## 1. The Role of Routing

In the previous blog we talked about different HTTP methods and how they are important in HTTP semantics. HTTP methods describe your **intent** — they express the **what** of a request. Your action, what you want to do on that particular resource, whether you want to fetch data, add data, update data, or delete data.

The role of routing is it expresses the **where** of a request. Where do you want to send your intention? Which resource do you want to perform your action on?

For example, you have a request with method **GET** — your intention is fetching data — and your route path is `/users`. The server sends you an array of users. You are saying: I want to fetch data, and from where? The `/users` resource. The combination of your intent (action) and your route (address) — the server takes these two and maps them to a particular handler or set of instructions. It performs all the business logic, database operations, and whatever is needed, and returns the data.

To summarize: **routing is mapping URL parameters to server-side logic**.

```mermaid
flowchart LR
    REQ["HTTP Request\n(Method + Route)"] --> ROUTER["Server Router"]
    ROUTER --> H1["Handler A"]
    ROUTER --> H2["Handler B"]
    ROUTER --> H3["Handler C"]
```

***

## 2. How Route Matching Works

The server first checks what the **method** is, then it checks the **route**. It concatenates these two to form a unique routing key. This is how the server differentiates between requests.

For example:

* **GET** `/api/books` — fetch all books
* **POST** `/api/books` — create a book

The route is the same, but the methods are different. The server concatenates method + route to form a unique path that maps to a specific handler. These two will never clash because the methods differentiate them.

```mermaid
sequenceDiagram
    participant C as Client
    participant S as Server

    C->>S: GET /api/books
    S->>S: Match: GET + /api/books
    S->>C: Return list of books

    C->>S: POST /api/books
    S->>S: Match: POST + /api/books
    S->>C: Create book, return all books
```

***

## 3. Static Routes

The first two examples — `/api/books` for GET and POST — are called **static routes**. They don't have any variable parameters inside the route. The path is a constant string that never changes. It always returns the same kind of data.

***

## 4. Dynamic Routes (Path Parameters)

A dynamic route contains variable parts. For example:

* **GET** `/api/users/123`

Here `123` is supposed to express the ID of the user. The application fetches details of one particular user using the ID inside the route parameter. The server extracts this ID from the route path and performs whatever operation it wants — fetching the user from the database and returning the details.

The convention used across servers (Java, Python, Node.js, Golang, Rust — it does not matter) is a **colon** before the parameter name to indicate it is dynamic:

```
GET /api/users/:id
```

When the request comes in as `/api/users/123`, the request gets routed to this handler. It matches the GET method, then `/api`, then `/users`, and then `123` gets inserted into the dynamic parameter slot called `id`.

The key thing: in route parameters, numbers, special characters — everything is converted into a string.

This is called a **path parameter** or **route parameter**. It is part of the route, right after the forward slash. Path parameters serve a **semantic expression** — you can read it like: "Get me some data, from `/api/users`, whose data? The user with ID 123." That is the whole idea of REST APIs — they provide a human-readable construct to routing.

***

## 5. Query Parameters

Path parameters serve a semantic purpose. But in GET requests, there is no body to send data. If you want to send additional values — like search terms, pagination, sorting — you use **query parameters**.

Query parameters are **key-value pairs** sent after a `?` in the URL:

```
GET /api/search?query=some+value
```

The server uses `/api/search` (including the method) to match the handler. The part after `?` — the key-value pair — is the query parameter.

Why not just put everything in the path? Because path parameters have a semantic meaning. `/api/users/123` means "fetch user 123." But `/api/search/some+value` defeats the purpose of REST semantics. Query parameters exist for metadata about the request.

### Pagination Example

A common use case is fetching paginated data:

```
GET /api/books
```

The server returns:

```json
{
  "data": [...books...],
  "total": 100,
  "currentPage": 1,
  "totalPages": 5,
  "limit": 20
}
```

The server sends 20 books by default and returns metadata about the response. To fetch page two:

```
GET /api/books?page=2
```

Other common query parameters: filtering by user-defined parameters, sort order (ascending/descending), and any other metadata about the request.

***

## 6. Nested Routes

Nested routing is not really a type of routing — it is a practice you will see everywhere because in REST APIs, for semantic expression, we often have to resort to nesting different types of resources.

For example:

```
GET /api/users/123/posts/456
```

Breaking it down:

* `/api/users` — static part, fetches all users
* `/api/users/123` — fetches details of user with ID 123
* `/api/users/123/posts` — fetches all posts of user 123
* `/api/users/123/posts/456` — fetches a specific post (ID 456) of user 123

Each level of nesting adds a new semantic meaning. The server matches the GET method and routes to a different handler at each level. You can stop at different phases to get different results.

```mermaid
flowchart TB
    A["GET /api/users"] --> B["All users"]
    C["GET /api/users/:id"] --> D["One user's details"]
    E["GET /api/users/:id/posts"] --> F["All posts of that user"]
    G["GET /api/users/:id/posts/:postId"] --> H["One specific post"]
```

This is pretty convenient and you will see it used pretty much everywhere if the API has even a medium level of complexity.

***

## 7. Route Versioning and Deprecation

Route versioning is a very common practice in REST API servers. For example:

```
GET /api/v1/products
GET /api/v2/products
```

If you look at the response of v1:

```json
{
  "data": [
    { "id": 1, "name": "Product", "price": 10 }
  ]
}
```

And v2:

```json
{
  "data": [
    { "id": 1, "title": "Product", "price": 10 }
  ]
}
```

In v1 the field was `name`, in v2 it is `title`. Usually versioning happens when new requirements come in. Let's say you were serving a web app and now you need to serve a React Native app. You had to change the response format.

Your first option is changing the whole route — doing `/api/new-products`. But what you can do instead is add versioning. In v1 you serve responses in one format, and in v2 you serve in another.

The benefits:

1. **Clear intention** — you have v1 response and v2 response, expressed explicitly
2. **No need to change the whole route** — you keep the same structure, just add a version
3. **Deprecation workflow** — you can eventually deprecate v1. Send a notice to frontend engineers that after v2 is released, v1 is deprecated. They have a window to migrate. Eventually you get rid of v1 and promote v2 to v1.

This gives you a very stable and complete workflow to add breaking changes to your API endpoints.

***

## 8. Catch-All Routes

If you send a request to a route the server does not serve — for example `/api/v3/products` when v3 does not exist — what happens?

After going through all the previous route matching algorithms, whatever request reaches the end gets mapped to a catch-all handler:

```
/*
```

This handler sends a user-friendly message that the route does not exist, instead of just sending a null response (the default behavior if you don't do catch-all handling).

```mermaid
flowchart LR
    REQ["GET /api/v3/products"] --> ROUTER["Server Router"]
    ROUTER -->|No match| CATCH["/* Catch-All Handler"]
    CATCH --> RESP["404: Route not found"]
```

***

## Resource

* [What is Routing in Backend? How Requests Find Their Way Home — Sriniously](https://www.youtube.com/watch?v=SubuU1iOC2s)
