Routing · Backend Development
What does a web server actually do with a request?
Course contentsWhy you need it
Every app you use talks to a backend: a program that receives requests, looks things up and sends answers. Flask, FastAPI, Express and Spring look different, but underneath they all do the same three things. Once you see them, any backend framework becomes easy to read.
The intuition
A request arrives with a method and a path: GET /users/42. The server:
- Routes it: finds the handler whose method and path pattern match.
/users/<id>matches/users/42withid = "42". - Runs the handler, which does the work.
- Responds with a status code and a body (usually JSON).
| Code | Means |
|---|---|
| 200 OK | here's what you asked for |
| 201 Created | I made the new thing |
| 400 Bad Request | your input is wrong |
| 401 / 403 | who are you? / you may not |
| 404 Not Found | no such thing |
| 405 Method Not Allowed | that path exists, but not with this method |
| 500 Internal Server Error | the server broke |
In Flask the same route looks like this; the framework does the matching for you:
@app.get("/users/<id>")
def get_user(id):
return {"id": int(id)}In this course we write the matching ourselves, as plain functions that return (status, body).
Definition
Route
A method plus a path pattern, connected to the handler that serves it: GET /users/<id> → get_user.
Handler
The function that does the work for one route and returns the response.
Status code
A three-digit number at the start of every response. 2xx success, 4xx the client's mistake, 5xx the server's.
Examples
One handler
Try it yourselfA handler looks something up and picks a status code.
USERS = {1: "Ada", 2: "Linus"}
def get_user(user_id):
if user_id not in USERS:
return 404, {"error": "no such user"}
return 200, {"id": user_id, "name": USERS[user_id]}
print(get_user(1))
print(get_user(7))(200, {'id': 1, 'name': 'Ada'})
(404, {'error': 'no such user'})
- L4Always handle the missing case first.
- L8Found: 200 and the user as a dictionary (it becomes JSON).
(200, {'id': 1, 'name': 'Ada'}) - L9Not found: 404 with an error message.
(404, {'error': 'no such user'})
Matching a path pattern
Try it yourselfSplit both into parts and compare them one by one; <name> parts capture a value.
def match(pattern, path):
want = pattern.strip("/").split("/")
got = path.strip("/").split("/")
if len(want) != len(got):
return None
params = {}
for w, g in zip(want, got):
if w.startswith("<") and w.endswith(">"):
params[w[1:-1]] = g
elif w != g:
return None
return params
print(match("/users/<id>", "/users/42"))
print(match("/users/<id>", "/posts/42"))
print(match("/users/<id>/posts", "/users/7/posts"))
print(match("/users/<id>", "/users/7/posts")){'id': '42'}
None
{'id': '7'}
None
- L2"/users/<id>" becomes ["users", "<id>"].
- L4Different number of parts: can't match.
- L8A <name> part matches anything and remembers it.
- L10A fixed part must be equal.
- L14id is captured as text: "42".
{'id': '42'}
A tiny router
Try it yourselfA list of routes, and a loop that finds the first match.
def match(pattern, path):
want = pattern.strip("/").split("/")
got = path.strip("/").split("/")
if len(want) != len(got):
return None
params = {}
for w, g in zip(want, got):
if w.startswith("<") and w.endswith(">"):
params[w[1:-1]] = g
elif w != g:
return None
return params
def health(params):
return 200, {"ok": True}
def get_user(params):
return 200, {"id": int(params["id"])}
ROUTES = [
("GET", "/health", health),
("GET", "/users/<id>", get_user),
]
def handle(method, path):
for m, pattern, handler in ROUTES:
params = match(pattern, path)
if params is not None and m == method:
return handler(params)
return 404, {"error": "not found"}
print(handle("GET", "/health"))
print(handle("GET", "/users/5"))
print(handle("GET", "/nope"))(200, {'ok': True})
(200, {'id': 5})
(404, {'error': 'not found'})
- L20Each route: method, pattern, handler.
- L26Try each route in order.
- L30Nothing matched.
Common mistakes
200 for everything
If errors come back as 200 with {"error": …}, clients, caches and monitoring all think it worked. Use the status code that matches what happened.
Forgetting path params are text
In /users/42, id arrives as the string "42". Convert it with int(), and answer 400 or 404 if that fails.
Order of routes
The first matching route wins. Put /users/me before /users/<id>, or "me" will be treated as an id.
Quick quiz
1. A client sends POST /orders and the order is saved. Which status fits best?
Show the answer
Answer: A
201 says a new resource was created; the body usually contains it, with its new id.
2. What does this print?
def match(pattern, path):
want = pattern.strip("/").split("/")
got = path.strip("/").split("/")
if len(want) != len(got):
return None
params = {}
for w, g in zip(want, got):
if w.startswith("<") and w.endswith(">"):
params[w[1:-1]] = g
elif w != g:
return None
return params
print(match("/shop/<item>/reviews", "/shop/lamp/reviews"))
print(match("/shop/<item>", "/shop"))Show the answer
Answer: A
lamp lands in <item>. "/shop" has one part and the pattern has two, so no match.
Practice
3 exercises in three steps: predict what a program prints, change a program to meet a new requirement, then write one from scratch. Your code runs in the browser and is checked the moment you submit.
- PredictRoute the requests
Type exactly what this prints.
- Modify405, not 404
When the path exists but the method doesn't, the server should say 405
{"error": "method not allowed"}, not 404. Changehandleso that:- path and method match → the handler's response
- the path matches some route, but no route with this method →
405 - no route has this path →
404
- WriteTyped path parameters
Write
match(pattern, path)like the one in the examples, plus one feature: a part written<int:name>only matches digits, and its value is converted to anint.match("/users/<int:id>", "/users/42") # {'id': 42} match("/users/<int:id>", "/users/me") # None match("/tags/<name>", "/tags/python") # {'name': 'python'}
Practise this lesson
Open the interactive lesson to edit and run these examples and solve the exercises with instant feedback. Free to start, no sign-up.
Terms in this lesson
| Term | Chinese | Definition |
|---|---|---|
| Backend | 后端 | The server side of an app: it handles requests and stores data. |
| Route | 路由 | A method and path pattern connected to a handler. |
| Status code | 状态码 | The number that says how a request went: 200, 404, 500… |