Skip to content

FastAPI

v0.115.8

Video walkthrough · 4 min · YouTubeFastAPI in 4 Minutes — Routing, Pydantic, Depends, CORS and Testing

FastAPI runs on Starlette for web routing and Pydantic for data validation. Python type hints define path parameters, query parameters, headers, and request bodies. FastAPI automatically validates incoming payloads against your types, converts inputs, returns JSON responses, and generates interactive OpenAPI (/docs) schemas at runtime.

ConceptUsage
Instantiationapp = FastAPI()
Operation@app.get("/items")
Routerrouter = APIRouter()
Include routerapp.include_router(router)

An application instance mounts routes via HTTP method decorators. Use APIRouter to split endpoints across modules.

from fastapi import FastAPI, APIRouter
app = FastAPI(title="Store API")
router = APIRouter(prefix="/users")
@router.get("/")
async def list_users():
return [{"id": 1, "name": "Ada"}]
app.include_router(router)

Tip: Pass tags=["users"] to APIRouter to organize route groups in the generated interactive /docs UI.

ParameterSyntax
Path parameterdef f(item_id: int):
Query parameterdef f(skip: int = 0):
Path validationitem_id: int = Path(gt=0)
Query validationq: str | None = Query(None)

Function parameters in route path templates are path parameters. Other parameters are inferred as query parameters.

from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., gt=0),
q: str | None = Query(None, max_length=50)
):
return {"item_id": item_id, "q": q}

Gotcha: A parameter with no default value is required. To make a query parameter optional, set a default like None.

FeatureCode
Request bodydef f(item: ItemModel):
Pydantic modelclass ItemModel(BaseModel):
Field rulesprice: float = Field(gt=0)
Nested modelstags: list[TagModel]

Declare request payloads as Pydantic models. FastAPI parses JSON request bodies, validates fields, and injects model instances.

from fastapi import FastAPI
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str
price: float = Field(..., gt=0)
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return {"name": item.name, "total": item.price}

Warning: Returning raw ORM objects without Pydantic models or response_model can accidentally expose internal fields.

ConstructPurpose
Depends(fn)Declare a function dependency
Shared logicDB session, auth, pagination
Sub-dependenciesDependencies depending on dependencies

Dependencies are callable objects or generators executed before route handlers. FastAPI caches dependency results per request.

from typing import Annotated
from fastapi import FastAPI, Depends
def common_params(
q: str | None = None, page: int = 1
):
return {"q": q, "page": page}
app = FastAPI()
@app.get("/items/")
async def list_items(
params: Annotated[dict, Depends(common_params)]
):
return params

Tip: Yielding inside a dependency (yield db) executes cleanup logic after the route handler completes.

MiddlewareFunction
HTTP middleware@app.middleware("http")
CORSCORSMiddleware
Custom processingInspect/modify requests & responses

Middlewares wrap request handling. CORSMiddleware configures cross-origin resource sharing headers for web clients.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"],
allow_methods=["*"],
allow_headers=["*"],
)

Gotcha: allow_origins=["*"] cannot be used alongside allow_credentials=True due to browser CORS security restrictions.

ToolUsage
TestClientSynchronous testing with httpx
AsyncClientAsynchronous test requests
Override depapp.dependency_overrides

Use httpx.AsyncClient or TestClient to test route endpoints. Override dependencies to swap mock services or test DBs.

from httpx import ASGITransport, AsyncClient
from fastapi import FastAPI
app = FastAPI()
@app.get("/ping")
async def ping():
return {"msg": "pong"}
async def test_ping():
tr = ASGITransport(app=app)
url = "http://testserver"
async with AsyncClient(tr, base_url=url) as c:
res = await c.get("/ping")
assert res.status_code == 200

Tip: Reset app.dependency_overrides = {} after tests to prevent state leakage between test cases.