Skip to main content

Featured

Backend Routing Architecture — HTTP Methods, Path vs Query Params (2026)

Day 15: The Switchboard — Routing, REST & Raw HTTP

  • Series: Logic & Legacy
  • Day 15 / 40
  • Level: Senior Architecture

Context: In Day 14, DNS translated our domain into an IP address. The user's packet has crossed the ocean and physically arrived at our server's TCP socket. Now what?

The Grand Illusion: How does an API differentiate GET vs POST?

Junior developers look at a URL endpoint like /api/books and see magic. They wonder: "How does the server know whether to fetch all the books or create a brand new book if the URL is exactly the same?"

A Senior Architect knows there is no magic. An HTTP request is literally just a massive string of raw text arriving over a TCP wire. It looks like this:

POST /api/books HTTP/1.1
Host: logicandlegacy.com
Authorization: Bearer token...

{"title": "1984", "author": "Orwell"}

The Router (the switchboard of your framework) takes this string, splits the very first line by spaces, and isolates two critical pieces of data: The Method (POST) and the Path (/api/books). By concatenating these into a unique lookup key in memory (e.g., "POST:/api/books"), it instantly knows exactly which Python function to execute. The URL is just text; the Router gives it meaning.

Dark neon-style backend architecture infographic showing how HTTP requests are routed using methods, paths, routers, static vs dynamic routes, REST verbs, and path/query parameters, with emphasis on why routing architecture matters for scalable APIs.


1. Static Routes vs Dynamic Routes

Not all routes are processed the same way under the hood. Frameworks segment them to maximize performance and save CPU cycles.

2. Under the Hood: Regex vs Radix Trees

How does the router actually extract variables from a dynamic route? It comes down to architectural algorithms.

The Regex Linear Search (Django & Early Flask)

Older frameworks use Regular Expressions. When you define a dynamic route, the framework compiles it into a Regex pattern. When a request arrives, the router iterates through a massive List of all your routes, running a regex .match() on each one until it finds a hit. This is an O(N) operation. If the route is at the bottom of the list, the lookup is objectively slower.

The Radix Tree (FastAPI & Modern Frameworks)

Modern, ultra-fast frameworks abandoned regex lists. They compile the URLs into a Radix Tree (Prefix Tree).

Instead of checking 500 routes, the router checks the first segment of the URL (e.g., /users). The tree instantly prunes away all routes that start with /products or /admin. It traverses down the tree node-by-node. The search time is O(K), where K is simply the length of the URL path. This is the secret to modern framework speed.

3. Deep Dive: Path Params vs Query Params

Hardcoding every possible URL is mathematically impossible. If you have a million users, you cannot register a million static routes. You must inject external data into your Python functions. You have two primary locations in the URL to extract this data, and confusing them is a sign of poor API design.

4. The RESTful Verbs (HTTP Methods)

The hallmark of a Senior Architect is obeying the laws of REST (Representational State Transfer). You do not encode actions into the URL (e.g., never use /create_user or /delete_user). The URL is simply the noun (/users). The HTTP Method is the verb.

HTTP Method Action / Intent Idempotent?
GET Read a resource. Never mutates the database. Yes (Safe to repeat)
POST Create a brand new resource. No (Repeating creates duplicates)
PUT Completely replace a resource (or create if missing). Yes (Overwrites with same data)
PATCH Partially update a resource (e.g., just changing an email). Usually No
DELETE Destroy a resource. Yes (Deleting twice does nothing)

🛠️ Day 15 Project: Building a Raw HTTP Router

I have uploaded a custom routing.py engine to the official repository. We didn't import FastAPI or Django; we built the underlying mechanics from scratch.

  • Observe the dispatch() method. See how it strips the raw TCP string (GET /api/books?sort=desc HTTP/1.1) into Method, Path, and Query String.
  • Review how we architecturally separate static_routes into a fast Dictionary and dynamic_routes into a Regex list to maximize lookup speed.
  • Run the script and watch the console output prove how one URL path triggers different Python functions depending solely on the HTTP Verb.
🔥 PRO UPGRADE: THE RADIX TREE REFACTOR

The current implementation in GitHub uses an O(N) Regex list for dynamic routes. Your Challenge: Refactor the HTTPRouter class to use a Dictionary-based Trie (Tree) structure. Instead of matching regex strings, the resolve() method should split the incoming URL by slashes (path.split('/')) and step down through the nested dictionaries. This will upgrade your engine to an O(K) lookup time, mimicking the speed of FastAPI.

View the Routing Engine on GitHub →

5. FAQ: API Design & Architecture

What is the difference between a 404 and a 405 status code?

A 404 Not Found means the URL Path (the Noun) does not exist in the router's registry at all. A 405 Method Not Allowed means the URL Path does exist, but you used the wrong HTTP Verb (e.g., trying to POST to an endpoint that only has a registered GET handler). A mature router handles this distinction automatically.

Should URLs have a trailing slash? (/users/ vs /users)

Historically, a trailing slash implied a directory, and no slash implied a file. In modern APIs, it is purely a stylistic choice. However, Strict Routing frameworks treat them as entirely different endpoints. To prevent broken links, modern routers implement an automatic HTTP 307 Redirect from the missing-slash version to the slashed version (or vice versa).

Why shouldn't I use a GET request with a JSON body payload?

While the HTTP specification technically does not forbid sending a JSON body with a GET request, many proxies, caching layers (like CDNs), and web servers will aggressively strip the body from a GET request before it even reaches your Python application. If you need to send a complex payload for a search query, use POST (often referred to as a "Search POST" pattern).

Comments