Programming with AI
ENRU
7.1

How a backend is built

request, handler, response

What it is

A backend is a program that waits for requests and answers them. Inside, it is always built the same way, in any language and any framework: a request arrives, the router decides who will handle it, the handler does the work, the response goes back.

Understand that chain once and you can find your way around any backend project — the words and the syntax change, the scheme stays.

What it looks like in code

Diagram from the book — to be redrawn for the web
The path of a request. The handler is the only place your own code lives; everything else is the framework.

Here is that scheme in code. A working example on FastAPI:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field

app = FastAPI()

SUBS: dict[int, list[str]] = {7: ["Oslo", "Bergen"]}


class SubOut(BaseModel):
    user_id: int
    cities: list[str]


@app.get("/subs/{user_id}", response_model=SubOut)
def get_subs(user_id: int) -> SubOut:
    if user_id not in SUBS:
        raise HTTPException(status_code=404, detail="user not found")
    return SubOut(user_id=user_id, cities=SUBS[user_id])

The request and the response, for real:

curl -i http://127.0.0.1:8765/subs/7
measured
HTTP/1.1 200 OK
server: uvicorn
content-length: 40
content-type: application/json

{"user_id":7,"cities":["Oslo","Bergen"]}
curl -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8765/subs/999
measured
404

How to read it

@app.get("/subs/{user_id}") is the route. The decorator registers the function: "call it on a GET request to this address" (article 2.9). Looking for a call to get_subs in the project is pointless — the framework calls it.

user_id: int is part of the address turned into an argument. The framework pulls it out of /subs/7 and converts it to a number. Send /subs/abc and the handler never even starts — a type error comes back instead.

response_model=SubOut is the shape of the response. The framework checks that this is what was returned and turns it into JSON.

raise HTTPException(404) is how you answer with an error. Not return but raise: the framework catches the exception and turns it into an HTTP response (article 2.8).

What the framework does and what you do

This is the key to reading other people's code:

The frameworkYou
accepts the connectiondecide what to do with the data
parses the URL and finds the handlergo to the database
checks types and schemasdecide what counts as an error
turns the result into JSON
catches exceptions and returns codes

Your code is one link in the chain. Which is why a backend project has many files, and the handlers are what you read first.

What to tell the AI

Prompt
Show every route in this project: method, address and the
function that handles it. As a table, no code.
Prompt
What happens on GET /subs/abc? Does the handler run, or does
the error come earlier?
Prompt
Add a handler GET /subs/{user_id}/count that returns the number
of subscriptions. 404 if the user doesn't exist. Don't touch
the other routes.

What to look for

Logic inside the handler. A handler should accept the request, call what is needed and return the result. If it holds fifty lines of SQL and business rules, that code cannot be reused or tested later.

Database access right in the handler. The same thing: queries are moved into a separate module so they can change without touching the routes.

A response with no schema. Without response_model the framework will return anything at all, including fields the user should not see.

return instead of raise on an error. Returning {"error": "not found"} with a 200 is lying to the client: formally everything is fine.

Cheat sheet

  • The chain is the same everywhere: client → route → handler → database → response.

  • Your code is only the handler; the framework does the rest.

  • A route is registered by a decorator; the call won't be found in the project.

  • An error is returned with raise, not return.

  • Read an unfamiliar backend from the list of routes — it is its contents page.

  • Logic and database queries are moved out of the handler.