cheat sheet

fastapi

Package-level reference for FastAPI on PyPI — install variants, version policy, the `[all]` extra, ecosystem companions, and alternatives.

#pip#package#web#async#apiupdated 05-31-2026

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

bash
pip install fastapi

Output: (none — exits 0 on success)

bash
uv add fastapi

Output: dependency resolved + added to pyproject.toml

bash
poetry add fastapi

Output: updated lockfile + virtualenv install

bash
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.1xx series (as of mid-2026). Like httpx, FastAPI has not yet shipped a 1.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.100 requires Pydantic v1; fastapi>=0.100 supports Pydantic v2 (and is the modern default).
  • Roadmap target is a 1.0 once 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] — includes uvicorn[standard], httpx (for TestClient), python-multipart (form data), email-validator (Pydantic EmailStr), and the fastapi CLI.
  • fastapi[all] — everything in standard plus jinja2, itsdangerous, pyyaml, ujson, orjson, and other optional integrations.

Common companion packages typically installed alongside:

  • uvicorn[standard] — ASGI server with uvloop + httptools
  • gunicorn — production process manager (wraps uvicorn workers)
  • pydantic — request/response models (pinned by FastAPI itself)
  • pydantic-settings.env and settings management
  • sqlalchemy / sqlmodel — ORM layer; sqlmodel is by the same author and pairs naturally
  • alembic — schema migrations for SQLAlchemy
  • httpxTestClient and outbound HTTP calls
  • python-jose / pyjwt — JWT auth helpers
  • passlib[bcrypt] — password hashing

Alternatives

PackageTrade-off
litestarFastAPI-style ergonomics with stricter typing and lower per-route overhead. Use for high-throughput APIs.
flaskSync, WSGI, minimal. Use for simple apps or when async is unnecessary.
django + DRFFull-stack with admin and ORM. Use when you need batteries included.
starletteThe ASGI toolkit FastAPI is built on. Use when you want routing without validation/OpenAPI overhead.
quartFlask-API-compatible but async. Use when migrating a Flask app to async incrementally.
robynRust-backed ASGI framework. Use for raw throughput at the cost of ecosystem maturity.

Common gotchas

  1. Pydantic v1 vs v2 split. fastapi<0.100 requires pydantic<2; fastapi>=0.100 requires pydantic>=2. Mixing produces opaque import-time errors. Pin both together.
  2. pip install fastapi alone does not include a server. You still need uvicorn (or another ASGI server like Hypercorn / Daphne) to actually run the app. Use fastapi[standard] if you want the server bundled.
  3. [all] is large. It pulls in ~20 transitive dependencies. Prefer [standard] for production unless you specifically need the extras.
  4. Pre-1.0 versioning. Minor releases occasionally adjust dependency-injection internals or response-model behavior. Read the changelog before bumping.
  5. fastapi CLI (shipped in [standard]) is the recommended entrypoint — fastapi dev app.py for development, fastapi run app.py for production. The old uvicorn app:app --reload pattern still works but is no longer the documented path.
  6. python-multipart is not a default dependency. Without it, UploadFile parameters silently 422 with a confusing error. fastapi[standard] pulls it in.
  7. OpenAPI 3.1 since 0.100. Some legacy tools still expect OpenAPI 3.0 — set FastAPI(openapi_version="3.0.2") if a consumer chokes on 3.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.

bash
# 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.

bash
# 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.

bash
# 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.

dockerfile
# 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):

python
# 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()
python
# 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_model with v2 rebuilds the model on every response; for hot paths, prefer returning a dict and using response_model_exclude_unset=True.
  • Annotated[Type, Depends(...)] style is now preferred over the positional Depends() default — the older form still works.
  • Lifespan events — pre-0.93 used @app.on_event("startup") / "shutdown"; modern apps use an asynccontextmanager passed as lifespan= to FastAPI(...).

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 OAuth2PasswordBearer for password-grant flows; verify tokens via python-jose or pyjwt with algorithms=["RS256"] (or HS256 with a strong secret). Never decode without verifying signature.
  • CORS. from fastapi.middleware.cors import CORSMiddleware — never use allow_origins=["*"] together with allow_credentials=True; the browser rejects it.
  • CSRF. FastAPI does NOT ship a CSRF middleware. For cookie-auth APIs, install fastapi-csrf-protect or 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 TrustedHostMiddleware with an explicit allow-list to block Host-header attacks.
  • HTTPS redirect. HTTPSRedirectMiddleware for environments where TLS termination is downstream of the app. Avoid double-redirects when the LB already enforces HTTPS.
  • Secret management. Use pydantic-settings to load SECRET_KEY, DB URLs, etc. from env vars; never commit to source.
  • Dependency CVEs. Watch starlette, pydantic, and python-multipart advisories — the multipart parser had a DoS CVE in 2024 fixed in 0.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.

python
# 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.

python
# 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.

python
# 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. uvicorn is canonical; granian (Rust) is faster but newer.
  • gunicorn — process manager for production; wraps uvicorn workers.
  • sqlalchemy 2.x / sqlmodel — sync or async ORM. sqlmodel shares Pydantic models with FastAPI routes.
  • alembic — schema migrations for SQLAlchemy.
  • asyncpg / psycopg[binary,pool] — async Postgres drivers; pair with SQLAlchemy AsyncEngine.
  • httpx — outbound HTTP calls AND TestClient backend.
  • celery / taskiq / arq — background job queues; taskiq is FastAPI-native, celery is 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.

python
# 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.

python
# 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.

python
# 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.

python
# 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 / SymptomLikely causeFix
pydantic.errors.PydanticUserError: A non-annotated attribute was detectedPydantic v1 model in v2-required FastAPIAdd type hint, or upgrade to v2; check fastapi>=0.100 pins pydantic>=2.
422 Unprocessable Entity on a valid-looking requestBody doesn't match the declared modelInspect .detail from the response; fields are case-sensitive; missing required field.
RuntimeError: Form data requires "python-multipart"Missing deppip install python-multipart or use fastapi[standard].
Slow startup with many modelsPydantic schema rebuildingUse model_config = ConfigDict(arbitrary_types_allowed=True) sparingly; profile with --reload-include.
WebSocketDisconnect not raising on browser closeClient never sent close frameAdd a ping/pong heartbeat; configure ws_ping_interval on uvicorn.
CORS preflight 400 from a browserCORSMiddleware not registeredAdd CORSMiddleware BEFORE routes; verify allow_origin_regex if using dynamic origins.
uvicorn.error: [Errno 98] Address already in usePrevious process didn't release portpkill -f uvicorn and retry; use SO_REUSEPORT for hot reload.
OpenAPI docs missing a routeRoute in a router not includedapp.include_router(my_router); check the prefix.

See also