Building APIs with FastAPI
FastAPI is a modern Python framework for building APIs. It uses type hints to validate requests, serialise responses and generate interactive OpenAPI documentation automatically. It suits microservices, ML model serving and any API-first project that doesn't need Django's full stack.
Setup
python -m venv .venv && source .venv/bin/activate
pip install "fastapi[standard]"
fastapi dev main.py # auto-reloading dev server on http://127.0.0.1:8000
A first API with Pydantic models
# main.py
from decimal import Decimal
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
app = FastAPI(title="Mithai Shop API", version="1.0.0")
class SweetIn(BaseModel):
name: str = Field(min_length=2, max_length=60)
price_per_kg: Decimal = Field(gt=0)
veg: bool = True
class SweetOut(SweetIn):
id: int
DB: dict[int, SweetOut] = {}
@app.get("/sweets", response_model=list[SweetOut])
def list_sweets(max_price: Decimal | None = None, limit: int = 20):
items = list(DB.values())
if max_price is not None:
items = [s for s in items if s.price_per_kg <= max_price]
return items[:limit]
@app.get("/sweets/{sweet_id}", response_model=SweetOut)
def get_sweet(sweet_id: int):
if sweet_id not in DB:
raise HTTPException(status_code=404, detail="Sweet not found")
return DB[sweet_id]
@app.post("/sweets", response_model=SweetOut, status_code=status.HTTP_201_CREATED)
def create_sweet(payload: SweetIn):
new_id = max(DB, default=0) + 1
DB[new_id] = SweetOut(id=new_id, **payload.model_dump())
return DB[new_id]
Open http://127.0.0.1:8000/docs and you get Swagger UI where you can try every endpoint.
Where parameters come from
| Declared as | FastAPI reads it from |
|---|---|
Name in the path {sweet_id} |
Path parameter |
Simple type not in path (limit: int = 20) |
Query string |
Pydantic model (payload: SweetIn) |
JSON body |
Header(), Cookie() |
Headers and cookies |
If a client sends {"name": "K", "price_per_kg": -5}, FastAPI returns 422 with a list of exactly which fields
failed and why — no validation code written by you.
Dependencies
Depends injects shared logic such as database sessions or authentication.
from fastapi import Depends, Header
def require_api_key(x_api_key: str = Header()):
if x_api_key != "load-from-env":
raise HTTPException(status_code=401, detail="Invalid API key")
@app.delete("/sweets/{sweet_id}", status_code=204, dependencies=[Depends(require_api_key)])
def delete_sweet(sweet_id: int):
DB.pop(sweet_id, None)
The header parameter x_api_key maps to the X-API-Key header automatically (underscores become hyphens).
FastAPI or Django REST Framework?
| FastAPI | DRF | |
|---|---|---|
| Strength | Speed, async, type-driven validation | Django ORM, admin, auth, mature ecosystem |
| Docs | Built-in OpenAPI | Via drf-spectacular |
| Best for | Microservices, ML inference | Full apps already on Django |
Key takeaways
- Type hints plus Pydantic models give you validation and docs for free.
- Use separate input and output models so internal fields never leak.
- Raise
HTTPExceptionwith the right status code; useDependsfor cross-cutting concerns. - Run with
fastapi run(Uvicorn) in production behind a reverse proxy.
Practice
Extend the sweets API with PATCH /sweets/{id} using a model whose fields are all optional, a category query
filter, and an API-key dependency on every write endpoint. Test each endpoint from the /docs page.