On this page
FastAPI
FastAPI is a modern Python web framework for building APIs with automatic validation, serialization, and interactive documentation — built on Starlette and Pydantic, with native async support.
Why FastAPI?
| Feature | Benefit |
|---|---|
| Type hints | Auto validation and IDE support |
| Pydantic models | Request/response serialization |
| Async/await | High concurrency for I/O-bound APIs |
| OpenAPI | Auto-generated docs at /docs |
| Performance | Comparable to Node.js and Go frameworks |
Quick Start
pip install fastapi uvicorn[standard]
# main.py
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI(title="User API", version="1.0.0")
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int | None = None
class UserResponse(BaseModel):
id: int
name: str
email: str
users_db: list[dict] = []
next_id = 1
@app.get("/health")
async def health():
return {"status": "ok"}
@app.get("/users", response_model=list[UserResponse])
async def list_users():
return users_db
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
global next_id
new_user = {"id": next_id, **user.model_dump()}
next_id += 1
users_db.append(new_user)
return new_user
@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
for user in users_db:
if user["id"] == user_id:
return user
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="User not found")
Run:
uvicorn main:app --reload --port 8000
# Docs: http://localhost:8000/docs
# ReDoc: http://localhost:8000/redoc
Path, Query, and Body Parameters
from fastapi import Query, Path
from typing import Annotated
@app.get("/products")
async def list_products(
page: Annotated[int, Query(ge=1)] = 1,
per_page: Annotated[int, Query(ge=1, le=100)] = 20,
category: Annotated[str | None, Query()] = None,
):
return {"page": page, "per_page": per_page, "category": category}
@app.get("/products/{product_id}")
async def get_product(
product_id: Annotated[int, Path(ge=1)],
):
return {"id": product_id}
Dependency Injection
FastAPI’s killer feature — reusable, testable dependencies:
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> dict:
token = credentials.credentials
user = verify_jwt(token) # your auth logic
if user is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
return user
@app.get("/me")
async def read_me(current_user: dict = Depends(get_current_user)):
return current_user
Database session dependency:
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/mydb")
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def get_db():
async with AsyncSessionLocal() as session:
yield session
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404)
return user
Async Database with SQLAlchemy 2.0
from sqlalchemy import select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
email: Mapped[str]
@app.post("/users", status_code=201)
async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db)):
db_user = User(name=user.name, email=user.email)
db.add(db_user)
await db.commit()
await db.refresh(db_user)
return db_user
Router Organization
Split large apps into modules:
# routers/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
async def list_users():
return []
# main.py
from routers import users
app.include_router(users.router)
Background Tasks
from fastapi import BackgroundTasks
def send_welcome_email(email: str):
# sync or async email sending
print(f"Sending welcome to {email}")
@app.post("/users", status_code=201)
async def create_user(
user: UserCreate,
background_tasks: BackgroundTasks,
):
# create user...
background_tasks.add_task(send_welcome_email, user.email)
return new_user
For heavy work, use Celery or ARQ instead.
Middleware
import time
from starlette.middleware.base import BaseHTTPMiddleware
class TimingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
start = time.perf_counter()
response = await call_next(request)
duration = time.perf_counter() - start
response.headers["X-Process-Time"] = str(duration)
return response
app.add_middleware(TimingMiddleware)
CORS:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://myapp.com"],
allow_methods=["*"],
allow_headers=["*"],
)
Testing
# test_main.py
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_health():
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_create_user():
response = client.post("/users", json={
"name": "Alice",
"email": "[email protected]",
})
assert response.status_code == 201
assert response.json()["name"] == "Alice"
Async tests with httpx:
import pytest
from httpx import AsyncClient, ASGITransport
from main import app
@pytest.mark.asyncio
async def test_list_users():
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
response = await client.get("/users")
assert response.status_code == 200
Production Deployment
# Gunicorn with Uvicorn workers
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000
Docker:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
FastAPI vs Django
| Use FastAPI when | Use Django when |
|---|---|
| API-only backend | Full web app with admin, templates |
| Async I/O heavy | Mature ORM, admin, ecosystem |
| Microservices | Monolith with many built-in features |
| Auto OpenAPI docs critical | Content management, forms |
FastAPI is the go-to choice for modern Python microservices and high-performance APIs.