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.