cheat sheet
fastapi
Package-level reference for FastAPI on PyPI — install variants, version policy, the `[all]` extra, ecosystem companions, and alternatives.
fastapi
What it is
fastapi is an ASGI web framework created by Sebastián Ramírez (tiangolo) in 2018. It pairs Pydantic for request/response validation with Starlette for the ASGI plumbing, and uses Python type hints to drive both runtime validation and OpenAPI schema generation. The project is now backed by FastAPI Labs and sponsored by Pulumi and others.
Reach for FastAPI when you want a typed REST API with auto-generated Swagger UI docs, dependency-injection ergonomics, and native async def route handlers. Reach for Litestar instead when you need stricter typing or higher per-route throughput; Flask or Django for sync, template-heavy apps.
Install
pip install fastapi
Output: (none — exits 0 on success)
uv add fastapi
Output: dependency resolved + added to pyproject.toml
poetry add fastapi
Output: updated lockfile + virtualenv install
pip install "fastapi[all]" # framework + uvicorn + every optional dep
pip install "fastapi[standard]" # framework + uvicorn + sensible defaults
Output: FastAPI plus the named bundle of optional dependencies
Versioning & Python support
- Current line is the
0.1xxseries (as of mid-2026). Like httpx, FastAPI has not yet shipped a1.0— minor releases can include behavioral changes, so pin a tight range in production. - Supports Python 3.8+ on recent releases; older floors have been dropped over time.
- Major dependency split:
fastapi<0.100requires Pydantic v1;fastapi>=0.100supports Pydantic v2 (and is the modern default). - Roadmap target is a
1.0once the Pydantic-v2 migration story is fully stabilized.
Package metadata
- Maintainer: Sebastián Ramírez (
tiangolo) and the FastAPI team - Project home: github.com/fastapi/fastapi
- Docs: fastapi.tiangolo.com
- PyPI: pypi.org/project/fastapi
- License: MIT
- Governance: FastAPI Labs (company-backed), community contributors
- First released: 2018
- Downloads: tens of millions per month; standard choice for new Python REST APIs
Optional dependencies & extras
fastapi[standard]— includesuvicorn[standard],httpx(forTestClient),python-multipart(form data),email-validator(PydanticEmailStr), and thefastapiCLI.fastapi[all]— everything instandardplusjinja2,itsdangerous,pyyaml,ujson,orjson, and other optional integrations.
Common companion packages typically installed alongside:
uvicorn[standard]— ASGI server with uvloop + httptoolsgunicorn— production process manager (wraps uvicorn workers)pydantic— request/response models (pinned by FastAPI itself)pydantic-settings—.envand settings managementsqlalchemy/sqlmodel— ORM layer;sqlmodelis by the same author and pairs naturallyalembic— schema migrations for SQLAlchemyhttpx—TestClientand outbound HTTP callspython-jose/pyjwt— JWT auth helperspasslib[bcrypt]— password hashing
Alternatives
| Package | Trade-off |
|---|---|
litestar | FastAPI-style ergonomics with stricter typing and lower per-route overhead. Use for high-throughput APIs. |
flask | Sync, WSGI, minimal. Use for simple apps or when async is unnecessary. |
django + DRF | Full-stack with admin and ORM. Use when you need batteries included. |
starlette | The ASGI toolkit FastAPI is built on. Use when you want routing without validation/OpenAPI overhead. |
quart | Flask-API-compatible but async. Use when migrating a Flask app to async incrementally. |
robyn | Rust-backed ASGI framework. Use for raw throughput at the cost of ecosystem maturity. |
Common gotchas
- Pydantic v1 vs v2 split.
fastapi<0.100requirespydantic<2;fastapi>=0.100requirespydantic>=2. Mixing produces opaque import-time errors. Pin both together. pip install fastapialone does not include a server. You still needuvicorn(or another ASGI server like Hypercorn / Daphne) to actually run the app. Usefastapi[standard]if you want the server bundled.[all]is large. It pulls in ~20 transitive dependencies. Prefer[standard]for production unless you specifically need the extras.- Pre-1.0 versioning. Minor releases occasionally adjust dependency-injection internals or response-model behavior. Read the changelog before bumping.
fastapiCLI (shipped in[standard]) is the recommended entrypoint —fastapi dev app.pyfor development,fastapi run app.pyfor production. The olduvicorn app:app --reloadpattern still works but is no longer the documented path.python-multipartis not a default dependency. Without it,UploadFileparameters silently 422 with a confusing error.fastapi[standard]pulls it in.- OpenAPI 3.1 since
0.100. Some legacy tools still expect OpenAPI 3.0 — setFastAPI(openapi_version="3.0.2")if a consumer chokes on3.1.
Production deployment
FastAPI is just an ASGI app — deployment is "pick an ASGI server and run it". The standard pattern is uvicorn workers managed by gunicorn (or uvicorn's own multi-worker mode) behind a reverse proxy. Cloud platforms (Cloud Run, Fly, Render, Railway) typically run a single uvicorn process per container and scale horizontally.
# Recommended for production: gunicorn + uvicorn workers
pip install "uvicorn[standard]" gunicorn
gunicorn app.main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--timeout 60 \
--graceful-timeout 30 \
--access-logfile - \
--max-requests 1000 \
--max-requests-jitter 50
Output: 4-worker server bound to :8000; each worker restarts after ~1000 requests to mitigate slow leaks.
# Lighter-weight: uvicorn alone with multiple workers (no gunicorn)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 --proxy-headers --forwarded-allow-ips='*'
Output: uvicorn spawns its own worker processes; --proxy-headers honors X-Forwarded-For from the upstream LB.
# fastapi CLI (recommended since fastapi[standard] shipped)
fastapi run app/main.py --workers 4 --port 8000
Output: equivalent to the uvicorn invocation; auto-detects whether app is exposed as a module-level variable.
# Containerized production image
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["fastapi", "run", "app/main.py", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Output: small slim image; one container per replica is the canonical pattern.
Worker-count guidance: roughly 2 × cpu_count() + 1 for CPU-light I/O-bound workloads; lower for memory-heavy models loaded per-worker. Behind a load balancer, set --proxy-headers and constrain --forwarded-allow-ips to the LB's CIDR so client IPs are trustworthy.
Version migration guide
The biggest break in the FastAPI timeline is the Pydantic v1 → v2 migration that landed in fastapi 0.100. Other minor releases occasionally tighten DI semantics or response-model serialization. Always pin both fastapi and pydantic together.
Pydantic v1 → v2 (FastAPI 0.100):
# Before (fastapi <0.100 + pydantic v1)
from pydantic import BaseModel, validator
class User(BaseModel):
name: str
class Config:
orm_mode = True
@validator("name")
def upper(cls, v): return v.upper()
# After (fastapi >=0.100 + pydantic v2)
from pydantic import BaseModel, field_validator, ConfigDict
class User(BaseModel):
name: str
model_config = ConfigDict(from_attributes=True) # was orm_mode
@field_validator("name")
@classmethod
def upper(cls, v: str) -> str: return v.upper()
Output: equivalent behavior on v2; the Config inner class and @validator shim still work via the v1 compatibility layer but are deprecated.
Other migration notes:
.dict()→.model_dump()and.json()→.model_dump_json()everywhere.response_modelwith v2 rebuilds the model on every response; for hot paths, prefer returning a dict and usingresponse_model_exclude_unset=True.Annotated[Type, Depends(...)]style is now preferred over the positionalDepends()default — the older form still works.- Lifespan events — pre-
0.93used@app.on_event("startup")/"shutdown"; modern apps use anasynccontextmanagerpassed aslifespan=toFastAPI(...).
Security considerations
FastAPI inherits Starlette's middleware model and Pydantic's validation; security failures are almost always the developer leaving safeguards off rather than missing features. Treat the checklist below as a default.
- Always validate request bodies via Pydantic models, not raw
dict. Untyped bodies bypass schema validation and accept arbitrary keys. - OAuth2 / JWT. Use
OAuth2PasswordBearerfor password-grant flows; verify tokens viapython-joseorpyjwtwithalgorithms=["RS256"](orHS256with a strong secret). Never decode without verifying signature. - CORS.
from fastapi.middleware.cors import CORSMiddleware— never useallow_origins=["*"]together withallow_credentials=True; the browser rejects it. - CSRF. FastAPI does NOT ship a CSRF middleware. For cookie-auth APIs, install
fastapi-csrf-protector use a same-site cookie + custom header check. - Rate limiting. Not built in; use
slowapi(Flask-Limiter port) or a reverse-proxy layer (nginx, Cloudflare, Envoy). - Trusted hosts. Add
TrustedHostMiddlewarewith an explicit allow-list to block Host-header attacks. - HTTPS redirect.
HTTPSRedirectMiddlewarefor environments where TLS termination is downstream of the app. Avoid double-redirects when the LB already enforces HTTPS. - Secret management. Use
pydantic-settingsto loadSECRET_KEY, DB URLs, etc. from env vars; never commit to source. - Dependency CVEs. Watch
starlette,pydantic, andpython-multipartadvisories — the multipart parser had a DoS CVE in 2024 fixed in0.0.7.
Testing & CI integration
FastAPI's TestClient wraps httpx and runs the ASGI app in-process. For async-aware tests, use httpx.AsyncClient directly with an ASGITransport.
# Sync TestClient — easiest, works for sync test functions
from fastapi.testclient import TestClient
from app.main import app
def test_health():
with TestClient(app) as client:
r = client.get("/health")
assert r.status_code == 200
assert r.json() == {"status": "ok"}
Output: test passes; startup/shutdown lifespan events fire inside the context manager.
# Async — when you need real `async def` test bodies
import httpx, pytest
from app.main import app
@pytest.mark.asyncio
async def test_health_async():
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
r = await c.get("/health")
assert r.status_code == 200
Output: same coverage, async-native.
# Override dependency for tests
from app.deps import get_db
from app.main import app
def fake_db(): yield FakeDB()
app.dependency_overrides[get_db] = fake_db
Output: all routes resolve get_db to fake_db until the override is cleared.
CI checklist: run pytest with -W error::DeprecationWarning to catch Pydantic v1 leakage early; cache the ~/.cache/pip directory; type-check with mypy app/ or pyright. httpx's ASGITransport is the only supported way to call into the app without a network socket.
Ecosystem integrations
The "FastAPI stack" rarely means just FastAPI — it's the validation/server/ORM/observability triad below. Pinning these together avoids the Pydantic-version drift that has bitten teams in the past.
pydantic+pydantic-settings— data validation + env/config loading. Required.uvicorn/hypercorn/granian— ASGI servers.uvicornis canonical;granian(Rust) is faster but newer.gunicorn— process manager for production; wraps uvicorn workers.sqlalchemy 2.x/sqlmodel— sync or async ORM.sqlmodelshares Pydantic models with FastAPI routes.alembic— schema migrations for SQLAlchemy.asyncpg/psycopg[binary,pool]— async Postgres drivers; pair with SQLAlchemyAsyncEngine.httpx— outbound HTTP calls ANDTestClientbackend.celery/taskiq/arq— background job queues;taskiqis FastAPI-native,celeryis the workhorse.opentelemetry-instrumentation-fastapi— tracing.sentry-sdk[fastapi]— error tracking.prometheus-fastapi-instrumentator— metrics endpoint.fastapi-users— drop-in auth (registration, login, OAuth, password reset).
Real-world recipes
Package-level patterns — lifespan wiring, dependency injection, websockets, background tasks. The companion python/fastapi.md covers routes and request handling; here we focus on cross-cutting concerns that the rest of the stack assumes.
# Recipe 1 — Lifespan with DB + Redis + background worker
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.db = await asyncpg.create_pool(os.environ["DB_URL"])
app.state.redis = redis.from_url(os.environ["REDIS_URL"])
yield
await app.state.db.close()
await app.state.redis.aclose()
app = FastAPI(lifespan=lifespan)
Output: resources opened on startup, closed on shutdown; available via request.app.state.
# Recipe 2 — Annotated dependency injection
from typing import Annotated
from fastapi import Depends, FastAPI
async def get_db(request: Request) -> asyncpg.Connection:
async with request.app.state.db.acquire() as conn:
yield conn
DBDep = Annotated[asyncpg.Connection, Depends(get_db)]
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: DBDep):
return await db.fetchrow("SELECT * FROM users WHERE id=$1", user_id)
Output: every route declaring db: DBDep gets a scoped connection from the pool.
# Recipe 3 — WebSocket with structured close
from fastapi import WebSocket, WebSocketDisconnect
@app.websocket("/ws")
async def ws_endpoint(ws: WebSocket):
await ws.accept()
try:
while True:
msg = await ws.receive_text()
await ws.send_text(f"echo: {msg}")
except WebSocketDisconnect:
pass
Output: echo server; WebSocketDisconnect lets you clean up without an unhandled exception.
# Recipe 4 — BackgroundTasks for fire-and-forget work
from fastapi import BackgroundTasks
@app.post("/signup")
async def signup(user: UserIn, bg: BackgroundTasks):
user_id = await create_user(user)
bg.add_task(send_welcome_email, user.email)
return {"id": user_id}
Output: the response returns immediately; the email send runs after the response is sent.
Troubleshooting common errors
| Error / Symptom | Likely cause | Fix |
|---|---|---|
pydantic.errors.PydanticUserError: A non-annotated attribute was detected | Pydantic v1 model in v2-required FastAPI | Add type hint, or upgrade to v2; check fastapi>=0.100 pins pydantic>=2. |
422 Unprocessable Entity on a valid-looking request | Body doesn't match the declared model | Inspect .detail from the response; fields are case-sensitive; missing required field. |
RuntimeError: Form data requires "python-multipart" | Missing dep | pip install python-multipart or use fastapi[standard]. |
| Slow startup with many models | Pydantic schema rebuilding | Use model_config = ConfigDict(arbitrary_types_allowed=True) sparingly; profile with --reload-include. |
WebSocketDisconnect not raising on browser close | Client never sent close frame | Add a ping/pong heartbeat; configure ws_ping_interval on uvicorn. |
| CORS preflight 400 from a browser | CORSMiddleware not registered | Add CORSMiddleware BEFORE routes; verify allow_origin_regex if using dynamic origins. |
uvicorn.error: [Errno 98] Address already in use | Previous process didn't release port | pkill -f uvicorn and retry; use SO_REUSEPORT for hot reload. |
| OpenAPI docs missing a route | Route in a router not included | app.include_router(my_router); check the prefix. |
See also
- Python: fastapi — API tutorial, routes, dependencies, examples
- Concept: API — REST design fundamentals
- Concept: async — async/await mental model
- Concept: HTTP — protocol fundamentals