When a client such as a web application, mobile app, or another service sends a request to a backend server, the server needs to understand what the client wants to do and where that request should go.
This is where HTTP methods and routing come into play.
A simple way to understand the difference is:
HTTP methods describe the “what” of a request, while routing describes the “where.”
For example, a GET request tells the server that the client wants to retrieve something. But the route tells the server what resource it wants to retrieve.
Let’s understand backend routing step by step.
What Is Routing?
In backend development, routing is the process of mapping an incoming request to the appropriate server-side logic or handler.
A request generally contains at least two important pieces of information:
- HTTP method — What action does the client want to perform?
- Route/path — Which resource or endpoint does the client want to access?
For example:
GET /api/users
Here:
GETrepresents the client’s intention to fetch data./api/usersrepresents the destination or resource.
The server receives this combination and uses it to determine which handler should process the request.
In simple terms:
Routing maps a URL path and HTTP method to server-side logic.
That handler may perform authentication, business logic, database operations, validation, and finally return a response to the client.
HTTP Method + Route = Routing Key
Consider these two requests:
GET /api/books
and:
POST /api/books
Notice that both requests use the same route:
/api/books
But their HTTP methods are different.
The first request means:
“I want to retrieve books.”
The second request means:
“I want to create or add a book.”
Therefore, the backend can treat them as two different operations.
Conceptually, the server uses the combination of:
HTTP Method + Route
to determine the appropriate handler.
So:
GET + /api/books → Get books handler
POST + /api/books → Create book handler
This is an important concept because the same URL can support multiple operations depending on the HTTP method.
1. Static Routes
The simplest type of route is a static route.
A static route contains a fixed path that does not change.
For example:
GET /api/books
Here, /api/books is fixed.
There is no variable value inside the route.
You could define another endpoint such as:
POST /api/books
The path remains the same, but the HTTP method changes the operation.
Static routes are called static because their path structure remains constant.
For example:
/api/books
/api/users
/api/products
/api/orders
Each of these represents a fixed route.
Static routes are particularly useful when the endpoint represents a collection or a fixed resource.
2. Dynamic Routes
What happens when we want to access one particular resource?
Suppose an application has thousands of users.
Instead of creating a separate route for every user, we can create a dynamic route:
GET /api/users/:id
Here, :id is a dynamic route parameter.
A real request might look like:
GET /api/users/123
In this case:
123
is the value of the id parameter.
The backend can extract this value and use it to find the corresponding user in a database.
Conceptually:
/api/users/:id
matches:
/api/users/123
/api/users/456
/api/users/789
The route stays the same structurally, while the value of id changes.
What Is a Path Parameter?
A dynamic value that appears directly inside the URL path is commonly called a path parameter or route parameter.
For example:
GET /api/users/123
can be represented by the server as:
GET /api/users/:id
When the request arrives, the server matches:
GET
/api/users
123
and assigns:
id = "123"
The handler can then use this value to perform the required operation, such as retrieving the user from a database.
Path parameters are especially useful when the parameter identifies a specific resource.
For example:
/api/users/123
clearly communicates:
Fetch the user whose ID is 123.
This makes API endpoints easier to understand and gives the URL a clear semantic meaning.
3. Query Parameters
Another important concept in routing is the query parameter.
Consider a search API:
GET /api/search?query=somevalue
Here:
/api/search
is the main route.
And:
?query=somevalue
contains the query parameter.
The general structure is:
/path?key=value
For example:
GET /api/search?query=laptop
Here:
key = query
value = laptop
Query parameters are commonly represented as key-value pairs.
Why Do We Need Query Parameters?
Query parameters are particularly useful when we want to send additional information about a request without changing the identity of the resource represented by the path.
For example, imagine an API that returns a list of books.
A simple request could be:
GET /api/books
But what if there are 1,000 books?
Returning all of them at once may not be practical.
The API could support pagination:
GET /api/books?page=2
Or:
GET /api/books?page=2&limit=20
Now the client is telling the server:
Give me page 2, with a limit of 20 items.
Query parameters can also be used for:
- Searching
- Filtering
- Sorting
- Pagination
- Controlling the number of returned results
For example:
GET /api/books?sort=price
or:
GET /api/books?category=technology
or:
GET /api/books?page=2&limit=20
The exact parameters depend on the API design.
Path Parameters vs Query Parameters
These two concepts are easy to confuse, so let’s compare them.
Path parameter
GET /api/users/123
Here, 123 identifies a specific user.
The meaning is:
Get the user with ID 123.
Query parameter
GET /api/users?role=admin
Here, role=admin provides additional criteria for the request.
The meaning is:
Get users filtered by the admin role.
A useful mental model is:
Path parameters identify resources.
Query parameters provide additional options, filters, or metadata for the request.
The source specifically emphasizes that path parameters are part of the route and are useful for semantic resource identification, while query parameters provide key-value information such as pagination, filtering, and sorting.
4. Nested Routes
As APIs become more complex, resources often have relationships with other resources.
For example:
User
└── Posts
└── Individual Post
A nested route can represent this relationship.
Consider:
GET /api/users/123/posts
This can be understood as:
Get all posts belonging to user 123.
We can go one level deeper:
GET /api/users/123/posts/456
This means:
Get post 456 belonging to user 123.
The structure becomes:
/api/users/:userId/posts/:postId
Here we have two dynamic path parameters:
userId
postId
For example:
GET /api/users/123/posts/456
means:
userId = 123
postId = 456
Nested routes are useful because they make relationships between resources visible directly in the URL.
Understanding Nested Routes Step by Step
Consider these three routes:
GET /api/users
Returns a list of users.
Then:
GET /api/users/123
Returns information about user 123.
Then:
GET /api/users/123/posts
Returns posts belonging to user 123.
Finally:
GET /api/users/123/posts/456
Returns a specific post 456 belonging to user 123.
Each additional level communicates more specific information.
This is why nested routing can be useful for expressing relationships between resources.
5. Route Versioning
APIs evolve over time.
Suppose your API originally returns products like this:
{
"id": 1,
"name": "Laptop",
"price": 50000
}
Later, you decide that the response should use title instead of name:
{
"id": 1,
"title": "Laptop",
"price": 50000
}
Changing the existing API response immediately could break applications that already depend on the old structure.
This is where API versioning becomes useful.
You could create:
GET /api/v1/products
and:
GET /api/v2/products
The first version can continue returning the old structure, while the second version provides the new structure.
For example:
/api/v1/products
might return:
{
"id": 1,
"name": "Laptop",
"price": 50000
}
while:
/api/v2/products
might return:
{
"id": 1,
"title": "Laptop",
"price": 50000
}
This allows existing clients to continue working while newer clients migrate to the updated API.
6. API Deprecation
Versioning is closely related to another important concept: deprecation.
Imagine that version 2 of your API has been released.
Instead of immediately deleting version 1, you can mark it as deprecated.
The process might look like this:
V1 → Existing API
V2 → New API
Clients are given time to migrate:
Client → V1
↓
Migration
↓
Client → V2
Once clients have migrated successfully, the development team can eventually remove support for V1.
This creates a safer way to introduce breaking changes without immediately disrupting existing clients.
The source describes versioning as a way to introduce a new API structure while giving client developers a window to migrate before an older version is eventually removed.
7. Catch-All Routes
What happens when a client requests an endpoint that doesn’t exist?
For example:
GET /api/v3/products
Suppose your server only supports:
/api/v1/products
/api/v2/products
There is no handler for:
/api/v3/products
Without appropriate handling, the server may return a generic response.
A catch-all route can provide a more useful response.
Conceptually, it looks something like:
/*
This means:
If the request doesn’t match any of the previously defined routes, handle it here.
The catch-all handler can return a friendly error such as:
{
"error": "Route not found"
}
This is useful because the client receives a clear indication that the requested endpoint does not exist.
A catch-all route is generally placed after the application’s normal routes so that valid routes are matched first.
Putting Everything Together
Let’s look at a small example API:
GET /api/books
POST /api/books
GET /api/books/:id
GET /api/search?query=value
GET /api/users/:userId/posts
GET /api/users/:userId/posts/:postId
GET /api/v1/products
GET /api/v2/products
*
Each route serves a different purpose.
| Route | Purpose |
|---|---|
GET /api/books |
Retrieve books |
POST /api/books |
Create a book |
GET /api/books/:id |
Retrieve one specific book |
GET /api/search?query=value |
Search using a query parameter |
GET /api/users/:userId/posts |
Retrieve posts belonging to a user |
GET /api/users/:userId/posts/:postId |
Retrieve a specific user’s post |
/api/v1/products |
Version 1 of the products API |
/api/v2/products |
Version 2 of the products API |
* |
Handle unmatched routes |
Together, these concepts form the foundation of backend routing.
A Simple Mental Model for Routing
Whenever you see an API request, break it into three questions:
1. What does the client want to do?
Look at the HTTP method.
GET → Retrieve
POST → Create
PUT → Update/replace
PATCH → Partially update
DELETE → Remove
2. Where does the request need to go?
Look at the route:
/api/users
/api/books
/api/products
3. Is there additional information?
Look for parameters.
Path parameter:
/api/users/123
Query parameter:
/api/users?role=admin
Nested parameters:
/api/users/123/posts/456
Once you understand these three pieces, reading backend routing code becomes much easier.
Final Takeaway
Routing is one of the fundamental concepts you need to understand before working with a backend codebase.
At its core:
Routing determines which server-side handler should process an incoming request based on the HTTP method and URL.
The most important concepts to remember are:
- Static routes — fixed paths such as
/api/books. - Dynamic routes — routes containing variable values such as
/api/users/:id. - Path parameters — values embedded directly in the URL path.
- Query parameters — key-value pairs used for things such as searching, filtering, sorting, and pagination.
- Nested routes — routes that represent relationships between resources.
- Route versioning — maintaining different API versions such as
/api/v1and/api/v2. - Deprecation — gradually moving clients from an older API version to a newer one.
- Catch-all routes — handling requests that don’t match any valid endpoint.
Once these concepts are clear, you have a strong foundation for understanding routing in frameworks such as Node.js, Python, Java, Go, Rust, and other backend technologies. The exact syntax may differ between frameworks, but the underlying routing concepts remain broadly similar.