Pydantic v2 is required — .dict() is now .model_dump(),
orm_mode is from_attributes=True.
@app.on_event("startup") is deprecated — use the
lifespan context manager. Annotated[T, Depends(…)]
is the modern dependency form. This sheet pins to behavior current as of May 2026.
Install · runSetup
bash
# Install
pip install "fastapi[standard]"
# ↑ bundles uvicorn, httpx, jinja2, python-multipart, email-validator
# Run (dev — auto-reload, docs at /docs)
fastapi dev app.py
# Run (prod)
fastapi run app.py --workers 4
# Or directly with uvicorn
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
# Alembic migrations (if using SQLAlchemy)
pip install alembic sqlalchemy "psycopg[binary]"
Where things liveCommon imports
from fastapi import FastAPI, APIRouter, Depends, HTTPException, status
from contextlib import asynccontextmanager
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
DB_URL = "sqlite+aiosqlite:///./app.db"
engine = create_async_engine(DB_URL)
Session = async_sessionmaker(engine, expire_on_commit=False)
class Base(DeclarativeBase): pass
class Note(Base):
__tablename__ = "notes"
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str] = mapped_column(index=True)
class NoteIn(BaseModel):
text: str = Field(min_length=1, max_length=200)
class NoteOut(NoteIn):
id: int
@asynccontextmanager
async def lifespan(app):
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
app = FastAPI(lifespan=lifespan)
async def db() -> AsyncSession:
async with Session() as s:
yield s
DB = Annotated[AsyncSession, Depends(db)]
@app.post("/notes", response_model=NoteOut, status_code=201)
async def create(payload: NoteIn, s: DB):
n = Note(text=payload.text)
s.add(n); await s.commit(); await s.refresh(n)
return n
@app.get("/notes/{nid}", response_model=NoteOut)
async def get(nid: int, s: DB):
n = await s.get(Note, nid)
if not n: raise HTTPException(404, "not found")
return n
Best practiceGood to know
Separate request models from response models.
Define ItemIn for what you accept and ItemOut for what you return. Pin response_model on every route — it filters extra fields and keeps the OpenAPI doc honest.
Use Annotated[T, Depends(…)], not default values.
It survives reordering arguments and lets you alias dependencies as types: DB = Annotated[AsyncSession, Depends(db)].
async def only if the handler is actually async.
A plain def handler runs on a threadpool — that’s correct for blocking libs (psycopg2, requests). An async def that calls blocking code stalls the entire event loop.
Common trapsWatch out for
BackgroundTasks isn’t a job queue.
It runs in the same process, after the response. A crash or restart loses the work. For anything important use Celery, RQ, or arq.
Don’t open a DB session per call inside a handler.
Use a yield-based dependency that opens once, commits/rolls back, and closes. Calling Session() directly in every handler leaks connections under load.
CORS preflights silently fail on missing methods.allow_origins=["*"] with allow_credentials=True is invalid — the browser drops the response. Either set explicit origins or drop credentials.
FastAPI is a modern Python web framework for building APIs with automatic OpenAPI documentation. It is built on Starlette and Pydantic, supports async/await natively, and generates interactive Swagger UI docs at /docs and ReDoc at /redoc without any extra configuration. It is one of the fastest Python frameworks available.
How does FastAPI use Pydantic?
FastAPI uses Pydantic models for request body validation, response serialisation, and settings management. Declare a class inheriting from pydantic.BaseModel, annotate fields with types, and FastAPI automatically validates incoming JSON against the schema, returning a 422 Unprocessable Entity response with error details on validation failure.
How does dependency injection work in FastAPI?
Declare a function with typed parameters and pass it to the Depends() constructor in a route signature. FastAPI resolves the dependency automatically at request time, injecting the return value into the route handler. Dependencies can be nested, can yield (for teardown), and can be scoped to a request, a router, or the entire application.
How does FastAPI handle async endpoints?
Define route handlers with async def to run them on the event loop without blocking. Use await for I/O-bound operations like database queries or HTTP calls to external services. For CPU-bound work, use run_in_executor to offload to a thread pool. Mixing sync and async is allowed; FastAPI runs sync handlers in a thread pool automatically.
How do I test a FastAPI app?
Use httpx.AsyncClient or the synchronous TestClient from fastapi.testclient to make requests against your app in tests. Instantiate TestClient(app) and call .get(), .post() etc. directly. Override dependencies with app.dependency_overrides to inject mocks. FastAPI works with pytest out of the box and integrates with pytest-asyncio for async tests.