Course outline

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.

About 5 min readContent reviewed
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.

python Complete in-process HTTP check
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)
Expected output
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.

python Complete ordered-route check
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)
Expected output
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.

Official references