Understanding HTTP Methods and Routing

HTTP methods describe your intent and express the “what” of a request—what action you want to perform on a particular resource. This could be fetching data, adding data, updating data, or deleting data.

Routing expresses the “where” of a request—which resource you want to perform your action on. You need to tell the server where you want to go.

For example, when you send a GET request to /users, you’re saying: “I want to fetch some data” (the method) “from the users resource” (the route). The server takes both the method and the route, maps them to a particular handler or set of instructions, performs the necessary business logic and database operations, and returns the data.

Routing is essentially the mapping of URL parameters to server-side logic.

Static Routes

Static routes are routes without any variable parameters inside them. They remain consistent and constant. Examples:

  • GET /api/books - Fetches an array of books
  • POST /api/books - Creates a new book resource

These routes never change. The string /api/books is always used when making the request, and it consistently returns the same type of data.

Important concept: The combination of method and route creates a unique path. GET /api/books and POST /api/books are two distinct endpoints that will never clash because the method differentiates them.

Dynamic Routes (Route Parameters/Path Parameters)

Dynamic routes contain variable parameters within the route path. Example:

GET /api/users/123

In this case, 123 represents the ID of a specific user. The application can fetch details of one particular user using the ID from the route parameter.

The server matches this pattern using a notation like:

GET /api/users/:id

The colon (:) convention indicates a dynamic parameter. This is an industry-wide practice across Java, Python, Node.js, Golang, and Rust. When a request comes in as /api/users/123, it gets routed to this handler because:

  • The method matches (GET)
  • The first part matches (/api)
  • The second part matches (/users)
  • The third part (123) gets inserted into the :id slot

This provides human-readable routing: “Fetch me data from /api/users for the user with ID 123.”

These dynamic parameters are called route parameters or path parameters because they appear after the forward slash as part of the route itself.

Query Parameters

Query parameters are key-value pairs sent in the request, typically used with GET requests. Example:

GET /api/search?query=some+value

The structure consists of:

  • The route: /api/search
  • The query separator: ?
  • The key-value pair: query=some+value

Why Query Parameters?

GET requests in REST APIs don’t have a body. While POST and PUT requests can send data in the request body, GET requests need an alternative way to send user-defined values to the server.

Path parameters serve a specific semantic purpose (identifying specific resources), but query parameters allow sending additional metadata about the request.

Practical Application: Pagination

Consider an endpoint that fetches a list of books:

GET /api/books

Response:

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

The server paginates the data, sending 20 books by default with metadata about the total. To fetch the next page:

GET /api/books?page=2

Common uses for query parameters:

  • Pagination (page number, limit)
  • Filtering (user-defined filters)
  • Sorting (order, direction: ascending/descending)

All this information is sent as key-value pairs in query parameters.

Nested Routes

Nested routes result from nesting different types of resources to express semantic meaning. Example:

GET /api/users/123/posts/456

This can be understood at multiple levels:

  1. /api/users - Static route returning all users
  2. /api/users/123 - Returns information about user 123
  3. /api/users/123/posts - Returns all posts by user 123
  4. /api/users/123/posts/456 - Returns post 456 by user 123

Each level expresses different semantic meaning:

  • First level: Fetch data related to this user
  • Second level: Fetch the posts of that user
  • Third level: Fetch a particular post from that user

Each combination creates a unique route that the server matches to different handlers, resulting in different responses. This nesting allows for clear, readable API structures that express complex relationships between resources.

Route Versioning and Deprecation

Route versioning is a common practice in REST API servers to manage breaking changes and API evolution. Example:

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

Version 1 Response:

{
  "data": [
    {"id": 1, "name": "Product A", "price": 100}
  ]
}

Version 2 Response:

{
  "data": [
    {"id": 1, "title": "Product A", "price": 100}
  ]
}

Notice the field changed from name to title.

Benefits of Versioning

  1. Clear intent: Explicitly shows which version of the API is being used
  2. No route changes needed: Instead of creating /api/new-products, you simply increment the version
  3. Graceful migration: Frontend engineers receive notice that v1 is deprecated and have a window to migrate to v2
  4. Stable workflow: Provides a complete workflow for adding breaking changes to API endpoints

The typical process: Release v2, deprecate v1, allow a migration window, eventually remove v1 and potentially rename v2 to v1 for the next cycle.

Catch-All Route

The catch-all route handles requests to endpoints that don’t exist on the server. Example request:

GET /api/v3/products

If the server doesn’t serve this route, after going through all route matching algorithms, the request reaches the catch-all handler:

* (or wildcard pattern)

Instead of sending a null response (the default behavior), the catch-all route sends a user-friendly message indicating that the requested route does not exist or was not found.

Summary

Understanding these routing concepts is essential for working with backend codebases:

  • Static routes for consistent endpoints
  • Dynamic routes with path parameters for resource identification
  • Query parameters for metadata and filtering
  • Nested routes for expressing resource relationships
  • Versioning for managing API evolution
  • Catch-all routes for handling non-existent endpoints

These concepts are fundamental to REST API design and are implemented across all major backend frameworks and languages.