FastAPI Cheat Sheet
Modern Python web framework • Fast • Type hints • Automatic docs • Async support • Dependency injection • Best practices
Sections
Quick Start & Setup
Install FastAPI • Create your first app • Run development server
Installation & Basic App
Install FastAPI and Uvicorn • Create minimal working application
# Installation
# pip install fastapi uvicorn[standard]
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
# Run with: uvicorn main:app --reload
# Visit: http://localhost:8000
# Docs: http://localhost:8000/docsApplication Configuration
Configure FastAPI with metadata • Custom OpenAPI docs • CORS setup
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(
title="My API",
description="API for managing items",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json"
)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production: specific domains
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)Path Operations & Parameters
HTTP methods • Path parameters • Query parameters • Request body
HTTP Methods (GET, POST, PUT, DELETE)
Define endpoints for CRUD operations • Type-safe parameters
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
description: str | None = None
# GET - Read
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
# POST - Create
@app.post("/items/")
async def create_item(item: Item):
return {"name": item.name, "price": item.price}
# PUT - Update
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, "item": item}
# DELETE - Delete
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"deleted": item_id}Path and Query Parameters
Extract parameters from URL • Optional and required parameters • Type validation
from fastapi import FastAPI, Query
from typing import Annotated
app = FastAPI()
# Path parameter with type
@app.get("/users/{user_id}")
async def read_user(user_id: int):
return {"user_id": user_id}
# Query parameters
@app.get("/items/")
async def read_items(
skip: int = 0,
limit: int = 10,
q: str | None = None
):
return {"skip": skip, "limit": limit, "q": q}
# Query parameter validation with Query
@app.get("/search/")
async def search(
q: Annotated[str, Query(
min_length=3,
max_length=50,
pattern="^[a-zA-Z0-9 ]+$"
)]
):
return {"query": q}
# Multiple path parameters
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(user_id: int, item_id: int):
return {"user_id": user_id, "item_id": item_id}Request Validation with Pydantic
Define request models • Field validation • Custom validators • Nested models
Pydantic Models
Define request/response schemas • Automatic validation and docs
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
app = FastAPI()
class User(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
full_name: str | None = None
age: int = Field(..., ge=0, le=150)
is_active: bool = True
created_at: datetime = Field(default_factory=datetime.now)
@app.post("/users/")
async def create_user(user: User):
return {
"username": user.username,
"email": user.email,
"age": user.age
}
# Field constraints:
# min_length, max_length - string length
# ge, gt, le, lt - number comparisons
# regex - pattern matchingCustom Validators
Add custom validation logic • Field and model validators
from fastapi import FastAPI
from pydantic import BaseModel, field_validator, model_validator
app = FastAPI()
class User(BaseModel):
username: str
password: str
password_confirm: str
@field_validator('username')
@classmethod
def username_alphanumeric(cls, v):
if not v.isalnum():
raise ValueError('must be alphanumeric')
return v
@field_validator('password')
@classmethod
def password_strength(cls, v):
if len(v) < 8:
raise ValueError('must be at least 8 characters')
if not any(c.isupper() for c in v):
raise ValueError('must contain uppercase')
return v
@model_validator(mode='after')
def passwords_match(self):
if self.password != self.password_confirm:
raise ValueError('passwords do not match')
return self
@app.post("/register/")
async def register(user: User):
return {"username": user.username}Nested Models
Complex data structures • Relationships • List and dict types
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
price: float
tags: list[str] = []
images: list[Image] = []
class Offer(BaseModel):
name: str
items: list[Item]
metadata: dict[str, str] = {}
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
# Example request body:
# {
# "name": "Special Deal",
# "items": [
# {
# "name": "Item 1",
# "price": 10.0,
# "tags": ["new", "sale"],
# "images": [{"url": "img.jpg", "name": "photo"}]
# }
# ]
# }Response Models & Status Codes
Define response schemas • Status codes • Multiple response types
Response Model
Define what API returns • Automatic filtering • Documentation
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
email: EmailStr
password: str
class UserOut(BaseModel):
username: str
email: EmailStr
# password excluded
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
# Password won't be in response
return user
# Response model excludes sensitive data
# Validates response matches schema
# Auto-generates response documentationStatus Codes
HTTP status codes • Custom responses • Error handling
from fastapi import FastAPI, status, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.post(
"/items/",
status_code=status.HTTP_201_CREATED
)
async def create_item(item: Item):
return item
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id not in items_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Item not found"
)
return items_db[item_id]
# Common status codes:
# 200 OK, 201 Created, 204 No Content
# 400 Bad Request, 401 Unauthorized, 403 Forbidden
# 404 Not Found, 422 Validation Error
# 500 Internal Server ErrorDependency Injection
Reusable dependencies • Database connections • Authentication • Shared logic
Basic Dependencies
Create reusable dependencies • Inject into path operations
from fastapi import FastAPI, Depends
app = FastAPI()
async def common_parameters(
skip: int = 0,
limit: int = 100,
q: str | None = None
):
return {"skip": skip, "limit": limit, "q": q}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return commons
# Dependency is called automatically
# Result is injected into function
# Reuse logic across endpointsDatabase Dependency
Inject database sessions • Automatic cleanup • Connection pooling
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from database import SessionLocal
app = FastAPI()
def get_db():
"""Database session dependency."""
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/{user_id}")
async def read_user(
user_id: int,
db: Session = Depends(get_db)
):
user = db.query(User).filter(User.id == user_id).first()
return user
# yield ensures cleanup happens
# db is automatically provided
# Connection is closed after requestSecurity & Authentication
OAuth2 • JWT tokens • API keys • Password hashing • Protected routes
OAuth2 with JWT
Token-based authentication • Login endpoint • Protected routes
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from datetime import datetime, timedelta
import jwt
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
class Token(BaseModel):
access_token: str
token_type: str
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=30)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
@app.post("/token", response_model=Token)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Verify credentials (simplified)
if form_data.username != "user" or form_data.password != "pass":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password"
)
access_token = create_access_token({"sub": form_data.username})
return {"access_token": access_token, "token_type": "bearer"}
async def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=401)
return username
except jwt.InvalidTokenError:
raise HTTPException(status_code=401)
@app.get("/users/me")
async def read_users_me(current_user: str = Depends(get_current_user)):
return {"username": current_user}Password Hashing
Secure password storage • bcrypt hashing • Verification
from fastapi import FastAPI
from passlib.context import CryptContext
from pydantic import BaseModel
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class User(BaseModel):
username: str
password: str
def hash_password(password: str) -> str:
"""Hash a password."""
return pwd_context.hash(password)
def verify_password(plain: str, hashed: str) -> bool:
"""Verify password against hash."""
return pwd_context.verify(plain, hashed)
@app.post("/register/")
async def register(user: User):
hashed_pw = hash_password(user.password)
# Save user with hashed_pw to database
return {"username": user.username}
# SECURITY: Never store plain passwords
# Use bcrypt, argon2, or scrypt
# Add salt automatically with these librariesDatabase Integration
SQLAlchemy ORM • CRUD operations • Async database • Migrations
SQLAlchemy Setup
Configure database • Define models • Create tables
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# Define model
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
username = Column(String)
hashed_password = Column(String)
# Create tables
Base.metadata.create_all(bind=engine)CRUD Operations
Create, Read, Update, Delete with SQLAlchemy • Type-safe queries
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
app = FastAPI()
# CREATE
@app.post("/users/")
def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = User(
email=user.email,
username=user.username,
hashed_password=hash_password(user.password)
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
# READ
@app.get("/users/{user_id}")
def read_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
# UPDATE
@app.put("/users/{user_id}")
def update_user(user_id: int, user: UserUpdate, db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.id == user_id).first()
if not db_user:
raise HTTPException(status_code=404)
db_user.username = user.username
db.commit()
return db_user
# DELETE
@app.delete("/users/{user_id}")
def delete_user(user_id: int, db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.id == user_id).first()
if not db_user:
raise HTTPException(status_code=404)
db.delete(db_user)
db.commit()
return {"deleted": user_id}Background Tasks & Files
Background task processing • File uploads • Async operations
Background Tasks
Execute tasks after returning response • Send emails • Data processing
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def send_email(email: str, message: str):
"""Background task to send email."""
# Simulate sending email
print(f"Sending email to {email}: {message}")
@app.post("/send-notification/")
async def send_notification(
email: str,
background_tasks: BackgroundTasks
):
background_tasks.add_task(send_email, email, "Thanks for signing up!")
return {"message": "Notification will be sent"}
# Task runs after response is sent
# Non-blocking for the client
# Use for emails, logging, cleanupFile Upload
Handle file uploads • Validate file types • Save files
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
"""Upload file as bytes."""
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
"""Upload with metadata."""
contents = await file.read()
# Save file
with open(f"uploads/{file.filename}", "wb") as f:
f.write(contents)
return {
"filename": file.filename,
"content_type": file.content_type,
"size": len(contents)
}
@app.post("/multiple-files/")
async def create_files(files: list[UploadFile]):
"""Upload multiple files."""
return {"filenames": [f.filename for f in files]}
# UploadFile is memory efficient (spooled)
# Access metadata: filename, content_type
# Validate file types before processingAdvanced Features
WebSockets • Middleware • Testing • Deployment strategies
WebSockets
Real-time bidirectional communication • Chat applications • Live updates
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message: {data}")
except:
await websocket.close()
# Client usage:
# const ws = new WebSocket("ws://localhost:8000/ws");
# ws.onmessage = (event) => console.log(event.data);
# ws.send("Hello");Custom Middleware
Process requests/responses • Add headers • Logging • Timing
from fastapi import FastAPI, Request
import time
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
# Middleware runs for every request
# Useful for logging, metrics, auth
# Can modify request and responseTesting with pytest
Test FastAPI endpoints • Test client • Fixtures
# test_main.py
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
def test_create_item():
response = client.post(
"/items/",
json={"name": "Test", "price": 10.0}
)
assert response.status_code == 200
assert response.json()["name"] == "Test"
# Run with: pytest
# TestClient doesn't require running server
# Use fixtures for database setup