Back to blog
2 min read

FastAPI in Practice: Types That Do the Work

  • FastAPI
  • Python
  • APIs

FastAPI's core idea is simple: the type hints you would write anyway become the contract for your API. Validation, serialization, and documentation all fall out of declaring what your data looks like. Here is what that buys you in practice.

Pydantic models are the request and response

You describe a payload as a Pydantic model, and FastAPI does the rest:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class CreateUser(BaseModel):
    email: str
    age: int
    is_active: bool = True

@app.post("/users")
def create_user(user: CreateUser):
    return {"email": user.email}

A request with a missing field, or a string where age should be an integer, is rejected with a clear 422 response before your function runs. You never write that validation by hand.

Declare a response_model and the output is filtered and validated the same way, so a password field cannot leak just because someone returned the wrong object.

Dependencies, not boilerplate

Cross-cutting concerns (a database session, the current user, pagination params) become dependencies:

from fastapi import Depends

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users/{user_id}")
def read_user(user_id: int, db = Depends(get_db)):
    return db.get(User, user_id)

Depends resolves the function, injects the result, and runs the cleanup after the response is sent. Dependencies can depend on other dependencies, so get_current_user can build on get_db without either one knowing about the route.

Async where it helps

FastAPI runs on ASGI, so route functions can be async:

@app.get("/data")
async def fetch_data():
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://example.com/api")
    return resp.json()

Use async def when the work is I/O bound and the libraries support it: HTTP calls, async database drivers. Use plain def when the work is blocking or the library is synchronous, and FastAPI will run it in a threadpool so it does not stall the event loop. Mixing the two incorrectly is the most common FastAPI performance mistake.

Documentation you did not write

Because every route declares its types, FastAPI generates an OpenAPI schema and serves interactive docs at /docs. The documentation cannot drift from the code, because it is derived from the code.

Why it adds up

None of these features are exotic. What makes FastAPI worth reaching for is that they share one source of truth: the type hints. Write the model once, and validation, serialization, docs, and editor autocomplete all follow. Less code, and the code that is left describes intent rather than plumbing.