FastAPI / Basic: HTTP and typed boundaries / Lesson 1
ASGI, HTTP, and route selection
Understand what FastAPI declares, what an ASGI server supplies, and how an in-process client verifies a complete HTTP contract.
On this page
Lesson goals
- Explain the assumptions and boundaries of asgi, http, and route selection.
- Interpret both complete examples and justify their observed outputs.
- Diagnose the listed failures and apply the worked solutions while keeping identity, state, and lifecycle ownership explicit.
Separate application from serving process
FastAPI builds an ASGI application: a callable that receives protocol events and produces responses. An ASGI server supplies real listening sockets, process management, and transport behavior. Defining app = FastAPI() does not start a network server. This course deliberately uses TestClient or an in-memory ASGI transport, so examples exercise routing and application behavior without opening a port. They cannot prove TLS, reverse-proxy, worker, or production network configuration. Keep those deployment responsibilities separate from the Python application contract, and never point a teaching client at a configured project or external service.
Read method, path, status, and body together
An HTTP operation is selected by method and path and has status, headers, and representation semantics. GET retrieves according to the API contract; POST and other methods need their own declared behavior. A path that does not exist usually yields 404, while a known path requested with an unsupported method can yield 405. The first example asserts a successful JSON response and an unknown-route response. Test the actual status and decoded content rather than assuming that a Python function returning a dictionary proves the whole HTTP boundary works.
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/health")
def health():
return {"ok": True}
with TestClient(app) as client:
response = client.get("/health")
print(response.status_code, response.json())
print(client.get("/missing").status_code)
200 {'ok': True}
404
The app returns HTTP 200 with one JSON boolean, and an undeclared path returns 404. TestClient invokes the app internally and opens no real listening port.
Treat route order as part of the contract
Routes are evaluated in declaration order where patterns overlap. A static path such as /items/latest should precede a dynamic /items/{item_id} path if latest is a reserved word. Otherwise the dynamic route can capture the text and fail integer validation before the static handler is reached. The second example declares the intended order and verifies both cases. Keep route naming and parameter types explicit, and review changes that broaden a path pattern. Avoid relying on an accidental validation failure as the only thing preventing a sensitive handler from being selected.
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/items/latest")
def latest(): return {"id": "latest"}
@app.get("/items/{item_id}")
def item(item_id: int): return {"id": item_id}
with TestClient(app) as client:
print(client.get("/items/latest").json()["id"], client.get("/items/7").json()["id"])
print(client.post("/items/latest").status_code)
latest 7
405
The static route is selected before the integer route. POST is unsupported for the static path and returns 405; no application mutation is defined.
Use Python types with the right expectations
Type annotations communicate intent to tooling, but ordinary Python calls do not automatically validate every annotated value. FastAPI inspects declarations to extract and validate supported request inputs and produce an API schema. Pydantic supplies the model validation and serialization layer used in later lessons. This is a framework boundary, not a promise that every helper function is runtime type-safe. Keep business functions small and explicit about their preconditions, then test them independently from HTTP. Separate transport parsing errors from domain decisions such as a missing resource or a denied operation.
Run the pinned teaching baseline
Examples are verified on Python 3.12.3 with FastAPI 0.162.0, Pydantic 2.14.1, Starlette 0.52.1, HTTPX 0.28.1, and AnyIO 4.15.1. These are a tested compatible baseline, not a claim that every package is the newest or suitable forever. Save a complete snippet as its shown Python file and execute it in an isolated environment with those dependencies. Use TestClient as a context manager when application lifespan matters. No Uvicorn process, socket listener, configured database, credential, or cloud service is started. Current official documentation should be consulted before changing versions because validation, lifecycle, and client integration can evolve.
Common errors and fixes
A reserved word returns 422
- Why it happens
- A dynamic route captured it before the static route.
- How to fix it
- Declare the specific static route before the overlapping pattern.
- Prevent it next time
- Test reserved and ordinary path values.
A dictionary return is called a complete HTTP test
- Why it happens
- Status, routing, and serialization were not exercised.
- How to fix it
- Use an isolated client and assert the real response contract.
- Prevent it next time
- Separate business-unit and transport tests.
Importing the app is assumed to start a server
- Why it happens
- ASGI application and serving process were conflated.
- How to fix it
- Treat transport and process hosting as a separate boundary.
- Prevent it next time
- Keep examples explicit about in-process execution.
Check your understanding
1. A known GET path receives POST. Which result distinguishes it from an unknown path?
Show worked solution
Under this declared app the unsupported method returns 405, while an unknown route returns 404. Test both instead of treating every unsuccessful request as missing data. HTTP routing decisions precede application resource lookup.
2. Why can an int annotation on a helper differ from an int path parameter?
Show worked solution
Ordinary Python calls do not enforce the annotation automatically. At the HTTP boundary, FastAPI extracts and validates the path value according to its declaration. Test helper preconditions and request validation separately so neither guarantee is assumed at the wrong layer.
Key takeaways
- FastAPI builds an ASGI application: a callable that receives protocol events and produces responses.
- Routes are evaluated in declaration order where patterns overlap.
- Examples are verified on Python 3.12.3 with FastAPI 0.162.0, Pydantic 2.14.1, Starlette 0.52.1, HTTPX 0.28.1, and AnyIO 4.15.1.