1. Quick Start
FastAPI is a modern, high-performance Python web framework for building APIs with standard Python type hints. It generates OpenAPI 3.1.0 schemas automatically, provides interactive API documentation out of the box, and achieves performance on par with Node.js and Go thanks to its foundation on Starlette (ASGI) and Pydantic (data validation).
Key Features:
- Automatic OpenAPI 3.1.0 schema generation from your code
- Interactive API docs: Swagger UI (
/docs) and ReDoc (/redoc) included by default - Full data validation via Pydantic models and Python type hints
- Dependency injection system for reusable logic across endpoints
- Built-in OAuth2, JWT, API key, and HTTP auth security schemes
- WebSocket support with the same decorator pattern as HTTP routes
- Server-Sent Events (SSE) via
EventSourceResponse - Background task execution without blocking responses
- File upload handling with
UploadFileandFile - Sub-application mounting and modular routing via
APIRouter - Async-first (
async def) with full sync (def) support - Automatic request/response serialization with JSON, ORJSON, UJSON
Prerequisites: Python 3.8+
Minimal setup (under 2 minutes):
pip install "fastapi[standard]"
Create main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
Run the development server:
fastapi dev main.py
INFO Using path main.py
INFO Resolved absolute path /home/user/app/main.py
INFO Searching for package file structure from directories with __init__.py files
INFO Importing from /home/user/app
╭─ FastAPI CLI - Development mode ─╮
│ │
│ Serving at: http://127.0.0.1:8000│
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for │
│ production use: fastapi run │
│ │
╰───────────────────────────────────╯
Open http://127.0.0.1:8000/docs to see interactive documentation for your API.
Tip
The
fastapi devcommand enables auto-reload by default. Every time you save a file, the server restarts automatically, making development fast and iterative.
2. Installation
Install Options
| Method | Command | What You Get |
|---|---|---|
| Standard (recommended) | pip install "fastapi[standard]" |
FastAPI + Uvicorn + fastapi CLI + email validation + multipart |
| Minimal | pip install fastapi |
FastAPI core only; bring your own ASGI server |
| With ORJSON | pip install "fastapi[standard]" orjson |
Faster JSON serialization via ORJSONResponse |
| With UJSON | pip install "fastapi[standard]" ujson |
Alternative JSON serialization via UJSONResponse |
Dependency Tree
FastAPI's core dependencies (installed automatically):
| Package | Purpose |
|---|---|
starlette |
ASGI framework (routing, middleware, requests, responses) |
pydantic |
Data validation and serialization via type hints |
typing_extensions |
Backported type hint features |
The [standard] extra adds:
| Package | Purpose |
|---|---|
uvicorn[standard] |
ASGI server for development and production |
fastapi-cli |
Provides fastapi dev and fastapi run CLI commands |
httpx |
Async HTTP client (used with TestClient) |
jinja2 |
Template rendering |
python-multipart |
File upload and form data parsing |
email-validator |
Email string validation for Pydantic models |
Important
If you install
fastapiwithout the[standard]extra, thefastapiCLI command is not available. You will need to run your app directly withuvicorn main:app --reloadinstead offastapi dev main.py.
Verifying the Installation
python -c "import fastapi; print(fastapi.__version__)"
0.135.3
fastapi --version
If the CLI is not installed, you will see:
RuntimeError: To use the fastapi command, please install "fastapi[standard]"
Running Your Application
| Environment | Command | Notes |
|---|---|---|
| Development | fastapi dev main.py |
Auto-reload enabled, binds to 127.0.0.1 |
| Development (manual) | uvicorn main:app --reload |
Equivalent to fastapi dev |
| Production | fastapi run main.py |
No auto-reload, binds to 0.0.0.0 |
| Production (manual) | uvicorn main:app --host 0.0.0.0 --port 8000 |
Full control over server settings |
| Production (Gunicorn) | gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker |
Multi-worker process manager |
3. Application Setup
FastAPI applications are configured through the FastAPI() constructor. There are no external configuration files or config keys; all settings are passed as Python arguments.
FastAPI Constructor Parameters
| Parameter | Type | Default | Purpose |
|---|---|---|---|
title |
str |
"FastAPI" |
API title shown in docs |
summary |
str | None |
None |
Short API summary for docs |
description |
str |
"" |
Detailed API description (supports Markdown) |
version |
str |
"0.1.0" |
API version shown in docs and OpenAPI schema |
debug |
bool |
False |
Enable debug mode (detailed error responses) |
openapi_url |
str | None |
"/openapi.json" |
OpenAPI schema endpoint; set to None to disable |
docs_url |
str | None |
"/docs" |
Swagger UI endpoint; set to None to disable |
redoc_url |
str | None |
"/redoc" |
ReDoc endpoint; set to None to disable |
default_response_class |
type[Response] |
JSONResponse |
Default response class for all endpoints |
redirect_slashes |
bool |
True |
Auto-redirect /items to /items/ (or vice versa) |
root_path |
str |
"" |
ASGI root path for reverse proxies |
root_path_in_servers |
bool |
True |
Include root_path in OpenAPI servers list |
lifespan |
Lifespan | None |
None |
Async context manager for startup/shutdown |
dependencies |
Sequence[Depends] | None |
None |
Global dependencies applied to all routes |
middleware |
Sequence[Middleware] | None |
None |
Middleware stack |
exception_handlers |
dict | None |
None |
Custom exception handlers |
responses |
dict | None |
None |
Additional OpenAPI responses for all endpoints |
callbacks |
list | None |
None |
OpenAPI callbacks |
webhooks |
APIRouter | None |
None |
OpenAPI webhooks |
terms_of_service |
str | None |
None |
URL to terms of service |
contact |
dict | None |
None |
Contact info for the API |
license_info |
dict | None |
None |
License info for the API |
openapi_tags |
list[dict] | None |
None |
Tag metadata for OpenAPI docs |
servers |
list[dict] | None |
None |
Server list for OpenAPI schema |
swagger_ui_parameters |
dict | None |
None |
Custom Swagger UI config parameters |
swagger_ui_oauth2_redirect_url |
str | None |
"/docs/oauth2-redirect" |
OAuth2 redirect URL for Swagger UI |
swagger_ui_init_oauth |
dict | None |
None |
OAuth2 init config for Swagger UI |
generate_unique_id_function |
Callable |
auto | Function to generate operation IDs |
separate_input_output_schemas |
bool |
True |
Generate separate schemas for input/output models |
strict_content_type |
bool |
True |
Enforce Content-Type header validation (CSRF protection) |
include_in_schema |
bool |
True |
Include this app in the OpenAPI schema |
deprecated |
bool | None |
None |
Mark all routes as deprecated in OpenAPI |
openapi_external_docs |
dict | None |
None |
External documentation link for OpenAPI |
Practical Setup Examples
Production-ready configuration:
from fastapi import FastAPI
app = FastAPI(
title="My Service API",
version="2.1.0",
description="Service for managing inventory and orders.",
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json",
root_path="/v2",
)
Disable all documentation endpoints (headless API):
app = FastAPI(
openapi_url=None, # disables /openapi.json, /docs, and /redoc
)
Note
Setting
openapi_url=Nonedisables the OpenAPI schema entirely, which also disables both Swagger UI and ReDoc since they depend on the schema. To disable only one docs interface, setdocs_url=Noneorredoc_url=Noneindividually.
Lifespan events (startup/shutdown):
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: initialize resources
db_pool = await create_db_pool()
app.state.db = db_pool
yield
# Shutdown: clean up resources
await db_pool.close()
app = FastAPI(lifespan=lifespan)
Warning
The
on_startupandon_shutdownevent handlers are deprecated. Use thelifespancontext manager instead. The lifespan approach properly handles resource sharing between startup and shutdown, and ensures cleanup runs even if startup fails partway through.
4. Core Usage
4.1 Defining Path Operations
Every endpoint in FastAPI is a "path operation" consisting of an HTTP method decorator and a function:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
async def list_items():
return [{"id": 1, "name": "Widget"}]
@app.post("/items", status_code=201)
async def create_item(name: str):
return {"id": 2, "name": name}
@app.put("/items/{item_id}")
async def update_item(item_id: int, name: str):
return {"id": item_id, "name": name}
@app.patch("/items/{item_id}")
async def partial_update(item_id: int, name: str | None = None):
return {"id": item_id, "name": name}
@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
return None
All available HTTP method decorators:
| Decorator | HTTP Method | Typical Use |
|---|---|---|
@app.get() |
GET | Retrieve resources |
@app.post() |
POST | Create resources |
@app.put() |
PUT | Full resource replacement |
@app.patch() |
PATCH | Partial updates |
@app.delete() |
DELETE | Remove resources |
@app.options() |
OPTIONS | CORS preflight, capability discovery |
@app.head() |
HEAD | Retrieve headers only (no body) |
@app.trace() |
TRACE | Diagnostic echo |
@app.api_route() |
Any/multiple | Register one function for multiple methods |
Using api_route for multiple methods:
@app.api_route("/health", methods=["GET", "HEAD"])
async def health():
return {"status": "ok"}
4.2 Path Parameters
Path parameters are extracted from the URL path and automatically converted to the declared Python type:
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id}
Requesting /users/42 returns {"user_id": 42}. Requesting /users/abc returns a 422 validation error automatically.
Path parameter validation with Path():
from fastapi import Path
@app.get("/items/{item_id}")
async def get_item(
item_id: int = Path(title="Item ID", ge=1, le=10000)
):
return {"item_id": item_id}
Predefined values with Enum:
from enum import Enum
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
return {"model": model_name.value}
File-path parameters (capturing slashes):
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"path": file_path}
Requesting /files/home/user/data.csv captures file_path = "home/user/data.csv".
4.3 Query Parameters
Any function parameter that is not a path parameter is automatically treated as a query parameter:
@app.get("/items")
async def list_items(skip: int = 0, limit: int = 10, q: str | None = None):
return {"skip": skip, "limit": limit, "q": q}
GET /items?skip=20&limit=5&q=widget parses all three parameters with type validation.
Query parameter validation with Query():
from fastapi import Query
@app.get("/search")
async def search(
q: str = Query(min_length=3, max_length=50, pattern="^[a-zA-Z0-9 ]+$"),
page: int = Query(default=1, ge=1),
size: int = Query(default=20, le=100),
):
return {"q": q, "page": page, "size": size}
Receiving a list of query parameters:
@app.get("/items")
async def filter_items(
tags: list[str] = Query(default=[])
):
return {"tags": tags}
GET /items?tags=python&tags=fastapi produces tags = ["python", "fastapi"].
Required query parameters (no default value):
@app.get("/items")
async def list_items(category: str): # required, no default
return {"category": category}
4.4 Request Body with Pydantic Models
Define request and response schemas with Pydantic models. FastAPI validates incoming JSON automatically:
from pydantic import BaseModel, Field
class ItemCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0)
description: str | None = None
tags: list[str] = []
class ItemResponse(BaseModel):
id: int
name: str
price: float
description: str | None
tags: list[str]
@app.post("/items", response_model=ItemResponse, status_code=201)
async def create_item(item: ItemCreate):
# item is already validated
return ItemResponse(id=1, **item.model_dump())
Sending the request:
curl -X POST http://127.0.0.1:8000/items \
-H "Content-Type: application/json" \
-d '{"name": "Widget", "price": 9.99, "tags": ["sale"]}'
{"id": 1, "name": "Widget", "price": 9.99, "description": null, "tags": ["sale"]}
Multiple body parameters:
from fastapi import Body
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: ItemCreate,
importance: int = Body(gt=0),
):
return {"item_id": item_id, "item": item, "importance": importance}
Expected body: {"item": {...}, "importance": 5}.
Embedding a single body parameter:
@app.post("/items")
async def create_item(item: ItemCreate = Body(embed=True)):
return item
Expected body: {"item": {...}} instead of just {...}.
4.5 Headers and Cookies
from fastapi import Header, Cookie
@app.get("/info")
async def info(
user_agent: str | None = Header(default=None),
accept_language: str | None = Header(default=None),
session_id: str | None = Cookie(default=None),
):
return {
"user_agent": user_agent,
"accept_language": accept_language,
"session_id": session_id,
}
Note
HTTP headers use hyphens (
User-Agent), but Python variables cannot. FastAPI automatically converts underscores to hyphens, souser_agentmaps to theUser-Agentheader. You can disable this withHeader(convert_underscores=False).
Duplicate headers (e.g., multiple X-Token values):
@app.get("/items")
async def read_items(x_token: list[str] | None = Header(default=None)):
return {"X-Token values": x_token}
4.6 Response Handling
Setting response status codes:
from fastapi import status
@app.post("/items", status_code=status.HTTP_201_CREATED)
async def create_item(item: ItemCreate):
return item
Custom response headers and cookies:
from fastapi import Response
@app.get("/custom")
async def custom_response(response: Response):
response.headers["X-Custom-Header"] = "custom-value"
response.set_cookie(key="session", value="abc123", httponly=True)
return {"message": "done"}
Available response classes:
| Class | Content-Type | Import |
|---|---|---|
JSONResponse |
application/json |
fastapi.responses |
HTMLResponse |
text/html |
fastapi.responses |
PlainTextResponse |
text/plain |
fastapi.responses |
RedirectResponse |
(redirect) | fastapi.responses |
StreamingResponse |
any | fastapi.responses |
FileResponse |
auto-detected | fastapi.responses |
ORJSONResponse |
application/json |
fastapi.responses (requires orjson) |
UJSONResponse |
application/json |
fastapi.responses (requires ujson) |
Returning a file:
from fastapi.responses import FileResponse
@app.get("/download")
async def download():
return FileResponse(
path="report.pdf",
filename="report.pdf",
media_type="application/pdf",
)
Streaming response:
from fastapi.responses import StreamingResponse
@app.get("/stream")
async def stream():
async def generate():
for i in range(100):
yield f"data: chunk {i}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
Controlling response model output:
@app.get("/items/{item_id}", response_model=ItemResponse)
async def get_item(item_id: int):
return {"id": item_id, "name": "Widget", "price": 9.99,
"internal_code": "W001"} # internal_code is filtered OUT
response_model filters the output to include only fields defined in ItemResponse. Use response_model_exclude_unset=True to omit fields the user did not set.
4.7 Form Data and File Uploads
Receiving form fields:
from fastapi import Form
@app.post("/login")
async def login(username: str = Form(), password: str = Form()):
return {"username": username}
Important
Form data and file uploads require the
python-multipartpackage. This is included infastapi[standard]but must be installed separately with the minimal install:pip install python-multipart.
Single file upload:
from fastapi import File, UploadFile
@app.post("/upload")
async def upload(file: UploadFile):
contents = await file.read()
return {
"filename": file.filename,
"content_type": file.content_type,
"size": len(contents),
}
Multiple file uploads:
@app.post("/upload-many")
async def upload_many(files: list[UploadFile]):
return {"filenames": [f.filename for f in files]}
File with form fields together:
@app.post("/submit")
async def submit(
title: str = Form(),
description: str = Form(default=""),
file: UploadFile = File(),
):
return {"title": title, "filename": file.filename}
UploadFile attributes and methods:
| Attribute/Method | Description |
|---|---|
filename |
Original filename from the client |
content_type |
MIME type (e.g., image/png) |
size |
File size in bytes (if available) |
headers |
Raw headers from the multipart section |
read(size) |
Read bytes (async) |
write(data) |
Write bytes (async) |
seek(offset) |
Seek to position (async) |
close() |
Close the file (async) |
4.8 Dependency Injection
FastAPI's dependency injection system lets you declare reusable logic that runs before your endpoint:
from fastapi import Depends
async def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users")
async def list_users(db: Session = Depends(get_db)):
return db.query(User).all()
Class-based dependencies:
class Pagination:
def __init__(self, skip: int = 0, limit: int = 100):
self.skip = skip
self.limit = limit
@app.get("/items")
async def list_items(pagination: Pagination = Depends()):
return {"skip": pagination.skip, "limit": pagination.limit}
Nested dependencies:
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = decode_token(token)
return user
async def get_active_user(user: User = Depends(get_current_user)):
if not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
return user
@app.get("/me")
async def read_me(user: User = Depends(get_active_user)):
return user
Global dependencies (applied to all routes):
app = FastAPI(dependencies=[Depends(verify_api_key)])
Router-level dependencies:
router = APIRouter(dependencies=[Depends(verify_admin)])
@router.get("/admin/stats")
async def admin_stats():
return {"users": 1000}
Dependencies with yield (resource cleanup):
Dependencies using yield execute code before AND after the endpoint. The code after yield runs during cleanup, even if the endpoint raises an exception:
async def get_db_session():
session = create_session()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
4.9 Error Handling
Raising HTTP exceptions:
from fastapi import HTTPException
@app.get("/items/{item_id}")
async def get_item(item_id: int):
if item_id not in items_db:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "Item lookup failed"},
)
return items_db[item_id]
Custom exception handlers:
from fastapi import Request
from fastapi.responses import JSONResponse
class ItemNotFoundError(Exception):
def __init__(self, item_id: int):
self.item_id = item_id
@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request: Request, exc: ItemNotFoundError):
return JSONResponse(
status_code=404,
content={"detail": f"Item {exc.item_id} does not exist"},
)
@app.get("/items/{item_id}")
async def get_item(item_id: int):
if item_id not in items_db:
raise ItemNotFoundError(item_id)
return items_db[item_id]
Overriding default validation error responses:
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
return PlainTextResponse(str(exc), status_code=422)
Overriding the default HTTPException handler:
from starlette.exceptions import HTTPException as StarletteHTTPException
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
4.10 Middleware
Middleware runs code before every request and after every response:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Built-in middleware options:
| Middleware | Purpose | Key Parameters |
|---|---|---|
CORSMiddleware |
Cross-Origin Resource Sharing | allow_origins, allow_methods, allow_headers, allow_credentials |
GZipMiddleware |
Compress responses | minimum_size (default 500 bytes) |
HTTPSRedirectMiddleware |
Redirect HTTP to HTTPS | (none) |
TrustedHostMiddleware |
Validate Host header | allowed_hosts |
Custom middleware (function-based):
import time
@app.middleware("http")
async def add_timing_header(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
elapsed = time.perf_counter() - start
response.headers["X-Process-Time"] = str(elapsed)
return response
4.11 Security and Authentication
FastAPI provides security utilities that integrate with the OpenAPI schema so the interactive docs show authentication controls.
OAuth2 with Password Bearer:
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
@app.post("/token")
async def login(form: OAuth2PasswordRequestForm = Depends()):
user = authenticate(form.username, form.password)
if not user:
raise HTTPException(status_code=401, detail="Invalid credentials")
token = create_access_token(user)
return {"access_token": token, "token_type": "bearer"}
@app.get("/users/me")
async def read_me(token: str = Depends(oauth2_scheme)):
user = decode_token(token)
return user
API Key authentication:
from fastapi.security import APIKeyHeader
api_key_header = APIKeyHeader(name="X-API-Key")
@app.get("/protected")
async def protected(api_key: str = Depends(api_key_header)):
if api_key != "secret-key":
raise HTTPException(status_code=403, detail="Invalid API key")
return {"data": "protected"}
All available security schemes:
| Class | Location | OpenAPI Type |
|---|---|---|
OAuth2PasswordBearer |
Authorization: Bearer <token> |
OAuth2 |
OAuth2AuthorizationCodeBearer |
Authorization: Bearer <token> |
OAuth2 (auth code flow) |
HTTPBasic |
Authorization: Basic <b64> |
HTTP |
HTTPBearer |
Authorization: Bearer <token> |
HTTP |
HTTPDigest |
Authorization: Digest <params> |
HTTP |
APIKeyQuery |
Query parameter | API Key |
APIKeyHeader |
Request header | API Key |
APIKeyCookie |
Cookie | API Key |
OpenIdConnect |
OpenID Connect discovery | OpenID Connect |
Using Security() with scopes:
from fastapi import Security
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="/token",
scopes={"items:read": "Read items", "items:write": "Write items"},
)
async def get_current_user(
security_scopes: SecurityScopes,
token: str = Depends(oauth2_scheme),
):
# Verify token has required scopes
...
@app.get("/items")
async def read_items(
user: User = Security(get_current_user, scopes=["items:read"])
):
return []
4.12 Background Tasks
Execute tasks after returning the response to the client:
from fastapi import BackgroundTasks
def send_notification(email: str, message: str):
# This runs in the background after response is sent
send_email(email, message)
@app.post("/orders")
async def create_order(order: Order, background_tasks: BackgroundTasks):
result = process_order(order)
background_tasks.add_task(send_notification, order.email, "Order received")
return result
Multiple background tasks run sequentially in the order they were added. They execute in the same process, so they share memory with the app.
Caution
Background tasks are not suitable for heavy, long-running computations. They run in the same event loop (for async tasks) or thread pool (for sync tasks). For heavy work, use a proper task queue like Celery, Dramatiq, or ARQ.
4.13 WebSockets
from fastapi import WebSocket, WebSocketDisconnect
@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
await ws.accept()
try:
while True:
data = await ws.receive_text()
await ws.send_text(f"Echo: {data}")
except WebSocketDisconnect:
print("Client disconnected")
WebSocket with path and query parameters:
@app.websocket("/ws/{room_id}")
async def chat(ws: WebSocket, room_id: int, token: str | None = None):
if not verify_token(token):
await ws.close(code=1008)
return
await ws.accept()
...
WebSocket with dependencies:
@app.websocket("/ws")
async def ws_endpoint(ws: WebSocket, db: Session = Depends(get_db)):
await ws.accept()
...
WebSocket methods:
| Method | Direction | Description |
|---|---|---|
accept() |
Server | Accept the connection |
close(code) |
Server | Close the connection |
send_text(data) |
Server -> Client | Send text message |
send_bytes(data) |
Server -> Client | Send binary message |
send_json(data) |
Server -> Client | Send JSON message |
receive_text() |
Client -> Server | Receive text message |
receive_bytes() |
Client -> Server | Receive binary message |
receive_json() |
Client -> Server | Receive JSON message |
4.14 Server-Sent Events (SSE)
FastAPI supports SSE through EventSourceResponse:
from fastapi.responses import EventSourceResponse, ServerSentEvent
import asyncio
@app.get("/events")
async def events():
async def event_generator():
for i in range(10):
yield ServerSentEvent(
data=f"Message {i}",
event="update",
id=str(i),
)
await asyncio.sleep(1)
return EventSourceResponse(event_generator())
4.15 Modular Applications with APIRouter
Organize large applications into separate files using APIRouter:
# routers/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
async def list_users():
return []
@router.get("/{user_id}")
async def get_user(user_id: int):
return {"id": user_id}
# main.py
from fastapi import FastAPI
from routers import users, items
app = FastAPI()
app.include_router(users.router)
app.include_router(items.router, prefix="/api/v1")
APIRouter supports all the same parameters as FastAPI for organizing endpoints:
| Parameter | Purpose |
|---|---|
prefix |
URL prefix for all routes in this router |
tags |
OpenAPI tags for documentation grouping |
dependencies |
Dependencies applied to all routes in this router |
responses |
Additional OpenAPI responses |
default_response_class |
Default response class for routes |
4.16 Sub-Applications
Mount independent FastAPI applications under a path:
main_app = FastAPI()
admin_app = FastAPI()
@admin_app.get("/dashboard")
async def dashboard():
return {"admin": True}
main_app.mount("/admin", admin_app)
The mounted app at /admin has its own independent OpenAPI docs at /admin/docs.
4.17 Static Files
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
4.18 Templates
from fastapi import Request
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
@app.get("/page", response_class=HTMLResponse)
async def page(request: Request):
return templates.TemplateResponse(
request=request,
name="page.html",
context={"title": "My Page"},
)
4.19 Dataclass Support
FastAPI supports Python dataclasses as request and response models:
from dataclasses import dataclass, field
@dataclass
class Item:
name: str
price: float
tags: list[str] = field(default_factory=list)
@app.post("/items")
async def create_item(item: Item):
return item
4.20 OpenAPI Customization
Tag metadata for documentation grouping:
tags_metadata = [
{"name": "users", "description": "Operations with users"},
{"name": "items", "description": "Manage items", "externalDocs": {
"description": "Items external docs",
"url": "https://example.com/items",
}},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/users", tags=["users"])
async def get_users():
return []
Webhooks:
webhooks = APIRouter()
@webhooks.post("new-subscription")
async def new_subscription(body: SubscriptionPayload):
"""Called when a new subscription is created."""
...
app = FastAPI(webhooks=webhooks)
Webhooks appear in the OpenAPI schema for client code generation but are not routable endpoints in your app.
Custom OpenAPI schema:
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
schema = get_openapi(
title=app.title,
version=app.version,
routes=app.routes,
)
schema["info"]["x-logo"] = {"url": "https://example.com/logo.png"}
app.openapi_schema = schema
return schema
app.openapi = custom_openapi
5. Common Tasks
5.1 CRUD API Pattern
A complete CRUD implementation with proper status codes and models:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
app = FastAPI()
class ItemCreate(BaseModel):
name: str
price: float
class ItemUpdate(BaseModel):
name: str | None = None
price: float | None = None
class ItemOut(BaseModel):
id: int
name: str
price: float
db: dict[int, dict] = {}
counter = 0
@app.post("/items", response_model=ItemOut, status_code=status.HTTP_201_CREATED)
async def create(item: ItemCreate):
global counter
counter += 1
record = {"id": counter, **item.model_dump()}
db[counter] = record
return record
@app.get("/items", response_model=list[ItemOut])
async def list_all(skip: int = 0, limit: int = 20):
return list(db.values())[skip : skip + limit]
@app.get("/items/{item_id}", response_model=ItemOut)
async def get_one(item_id: int):
if item_id not in db:
raise HTTPException(404, "Item not found")
return db[item_id]
@app.patch("/items/{item_id}", response_model=ItemOut)
async def update(item_id: int, updates: ItemUpdate):
if item_id not in db:
raise HTTPException(404, "Item not found")
stored = db[item_id]
for key, val in updates.model_dump(exclude_unset=True).items():
stored[key] = val
return stored
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete(item_id: int):
if item_id not in db:
raise HTTPException(404, "Item not found")
del db[item_id]
5.2 Database Integration (SQLAlchemy)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session, DeclarativeBase
from fastapi import Depends
engine = create_engine("sqlite:///./app.db", connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine)
class Base(DeclarativeBase):
pass
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users")
def list_users(db: Session = Depends(get_db)):
return db.query(UserModel).all()
5.3 Database Integration (SQLModel)
SQLModel combines SQLAlchemy and Pydantic in a single model:
from sqlmodel import SQLModel, Field, Session, create_engine, select
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
engine = create_engine("sqlite:///heroes.db")
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
@app.post("/heroes", response_model=Hero)
def create_hero(hero: Hero, session: Session = Depends(get_session)):
session.add(hero)
session.commit()
session.refresh(hero)
return hero
@app.get("/heroes", response_model=list[Hero])
def list_heroes(session: Session = Depends(get_session)):
return session.exec(select(Hero)).all()
5.4 JWT Authentication
from datetime import datetime, timedelta
from jose import jwt, JWTError
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
def create_access_token(data: dict):
to_encode = data.copy()
to_encode["exp"] = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
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, detail="Invalid token")
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
user = get_user_from_db(username)
if user is None:
raise HTTPException(status_code=401, detail="User not found")
return user
@app.get("/protected")
async def protected_route(user: User = Depends(get_current_user)):
return {"user": user.username}
5.5 CORS for Frontend Integration
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"https://myapp.example.com",
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
expose_headers=["X-Total-Count"],
max_age=600,
)
5.6 Rate Limiting Pattern
FastAPI does not include built-in rate limiting, but you can implement it with middleware or dependencies:
from collections import defaultdict
import time
from fastapi import Request, HTTPException
request_counts: dict[str, list[float]] = defaultdict(list)
async def rate_limit(request: Request):
client_ip = request.client.host
now = time.time()
window = 60 # seconds
max_requests = 100
# Remove old entries
request_counts[client_ip] = [
t for t in request_counts[client_ip] if now - t < window
]
if len(request_counts[client_ip]) >= max_requests:
raise HTTPException(status_code=429, detail="Rate limit exceeded")
request_counts[client_ip].append(now)
app = FastAPI(dependencies=[Depends(rate_limit)])
5.7 Testing FastAPI Applications
FastAPI provides a TestClient built on httpx:
from fastapi.testclient import TestClient
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": "Widget", "price": 9.99})
assert response.status_code == 201
assert response.json()["name"] == "Widget"
def test_validation_error():
response = client.post("/items", json={"name": "", "price": -1})
assert response.status_code == 422
def test_not_found():
response = client.get("/items/99999")
assert response.status_code == 404
Async testing with pytest-asyncio and httpx:
import pytest
from httpx import AsyncClient, ASGITransport
@pytest.mark.anyio
async def test_async():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.get("/")
assert response.status_code == 200
Overriding dependencies in tests:
def get_test_db():
return test_database
app.dependency_overrides[get_db] = get_test_db
client = TestClient(app)
response = client.get("/users")
# Clean up
app.dependency_overrides.clear()
5.8 Pagination Pattern
from pydantic import BaseModel
from fastapi import Query
class PaginatedResponse(BaseModel):
items: list
total: int
page: int
size: int
pages: int
@app.get("/items", response_model=PaginatedResponse)
async def list_items(
page: int = Query(default=1, ge=1),
size: int = Query(default=20, ge=1, le=100),
):
total = len(all_items)
start = (page - 1) * size
items = all_items[start : start + size]
return PaginatedResponse(
items=items,
total=total,
page=page,
size=size,
pages=(total + size - 1) // size,
)
5.9 Request Validation Patterns
Nested models:
class Address(BaseModel):
street: str
city: str
zip_code: str = Field(pattern=r"^\d{5}(-\d{4})?$")
class UserCreate(BaseModel):
name: str
email: str
addresses: list[Address] = []
Custom validators:
from pydantic import field_validator
class Order(BaseModel):
quantity: int
unit_price: float
@field_validator("quantity")
@classmethod
def quantity_must_be_positive(cls, v):
if v <= 0:
raise ValueError("Quantity must be positive")
return v
Model with examples for OpenAPI docs:
class Item(BaseModel):
name: str
price: float
model_config = {
"json_schema_extra": {
"examples": [
{"name": "Widget", "price": 9.99},
{"name": "Gadget", "price": 24.99},
]
}
}
5.10 Deploying Behind a Reverse Proxy
When deploying behind Nginx, Traefik, or similar:
app = FastAPI(root_path="/api/v1")
Or pass it at the ASGI server level:
uvicorn main:app --root-path /api/v1
This ensures the OpenAPI schema and documentation URLs correctly reflect the proxy prefix.
5.11 Multi-Worker Production Deployment
Using Gunicorn with Uvicorn workers:
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Docker deployment:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["fastapi", "run", "main.py", "--port", "8000"]
Tip
For multi-worker deployments, avoid storing state in module-level variables (they are not shared between workers). Use a database, Redis, or similar shared store.
5.12 Structured Logging
import logging
from fastapi import Request
logger = logging.getLogger("myapp")
@app.middleware("http")
async def log_requests(request: Request, call_next):
logger.info(f"{request.method} {request.url.path}")
response = await call_next(request)
logger.info(f"Response: {response.status_code}")
return response
5.13 Health Check Endpoint
@app.get("/health")
async def health():
return {"status": "healthy"}
@app.get("/ready")
async def readiness(db: Session = Depends(get_db)):
try:
db.execute(text("SELECT 1"))
return {"status": "ready"}
except Exception:
raise HTTPException(503, "Database unavailable")
6. Troubleshooting
Installation Issues
| # | Problem | Solution |
|---|---|---|
| 1 | RuntimeError: To use the fastapi command, please install "fastapi[standard]" |
Run pip install "fastapi[standard]" instead of pip install fastapi |
| 2 | ModuleNotFoundError: No module named 'pydantic' |
FastAPI requires Pydantic; reinstall with pip install fastapi to pull dependencies |
| 3 | pip install fastapi[standard] fails in zsh |
Quote the brackets: pip install "fastapi[standard]" (zsh interprets brackets as glob) |
| 4 | Conflicting Pydantic versions | FastAPI requires Pydantic v2; run pip install "pydantic>=2.0" to upgrade |
| 5 | ImportError: cannot import name 'PydanticV1NotSupportedError' |
You are using Pydantic v1 which is no longer supported; upgrade to Pydantic v2 |
Server and Runtime Issues
| # | Problem | Solution |
|---|---|---|
| 6 | Address already in use when starting |
Another process is using port 8000; kill it or use --port 8001 |
| 7 | Server starts but no auto-reload | Use fastapi dev (not fastapi run) or add --reload flag to uvicorn |
| 8 | asyncio.run() cannot be called from a running event loop |
You are calling sync code inside an async endpoint; use await or run in executor |
| 9 | Slow response with sync def endpoints |
Sync endpoints run in a thread pool (limited threads); switch to async def for I/O-bound work, or increase thread pool size |
| 10 | uvicorn not found after install |
Ensure your virtual environment is activated; pip install uvicorn[standard] |
Request Handling Issues
| # | Problem | Solution |
|---|---|---|
| 11 | 422 Unprocessable Entity on POST requests |
Request body does not match the Pydantic model; check the detail array in the response for specific field errors |
| 12 | 422 error when sending form data |
Ensure you are using Form() parameters, not a Pydantic body model. Also install python-multipart |
| 13 | Query parameters not received | Parameters with default values of None are optional; required params have no default. Check parameter names match the URL query string |
| 14 | 405 Method Not Allowed |
You are using the wrong HTTP method (e.g., GET instead of POST). Check the decorator |
| 15 | Path parameters received as strings despite int annotation |
This should not happen with FastAPI; verify the path parameter syntax is {param_name} and the annotation is correct |
| 16 | 307 Temporary Redirect on POST |
FastAPI's redirect_slashes=True (default) redirects /items to /items/; on redirect, POST becomes GET. Define your route with a trailing slash or set redirect_slashes=False |
| 17 | Request body is empty/None | Ensure Content-Type: application/json header is set. With strict_content_type=True (default), missing or wrong Content-Type causes rejection |
| 18 | File uploads fail silently | Verify python-multipart is installed and the parameter uses UploadFile or File() |
| 19 | Large file upload times out | Configure your ASGI server timeout; Uvicorn default is no timeout but reverse proxies may have limits |
| 20 | Duplicate query parameter values lost | Use list[str] type annotation with Query() to receive all values |
CORS and Security Issues
| # | Problem | Solution |
|---|---|---|
| 21 | CORS errors in browser | Add CORSMiddleware with the correct allow_origins. Wildcard ["*"] works for dev but specify exact origins for production |
| 22 | CORS preflight fails despite middleware | Ensure allow_methods includes the method being used and allow_headers includes custom headers. If using credentials, allow_origins cannot be ["*"] |
| 23 | OAuth2 "Not authenticated" in Swagger UI | Click the "Authorize" button in Swagger UI and enter your token before making requests |
| 24 | Cookies not sent in cross-origin requests | Set allow_credentials=True in CORS middleware and use withCredentials: true on the client side |
Response and Serialization Issues
| # | Problem | Solution |
|---|---|---|
| 25 | datetime objects serialized as strings unexpectedly |
This is correct behavior; JSON does not have a datetime type. Pydantic serializes to ISO 8601 strings |
| 26 | Response includes extra fields not in response_model |
This should not happen; response_model filters output. Verify you are using response_model= in the decorator, not just the return type hint |
| 27 | response_model causes performance issues |
For large responses, use response_model_exclude_unset=True to skip unnecessary validation. Or return a Response directly |
| 28 | Binary data in JSON response | Return binary content using StreamingResponse or FileResponse instead of JSONResponse |
| 29 | ORJSONResponse raises ImportError |
Install orjson: pip install orjson |
| 30 | Nested model fields show as null despite being set |
Check that nested Pydantic models use proper type annotations; Optional[Model] requires explicit None default |
Testing Issues
| # | Problem | Solution |
|---|---|---|
| 31 | TestClient not found |
Install httpx: pip install httpx (included with fastapi[standard]) |
| 32 | Tests fail with dependency injection | Use app.dependency_overrides[dependency_func] = mock_func to replace dependencies in tests |
| 33 | Async tests fail | Use pytest-anyio and httpx.AsyncClient with ASGITransport for async test support |
| 34 | WebSocket tests hang | Use with client.websocket_connect("/ws") as ws: context manager pattern |
| 35 | File upload tests fail | Pass files as files={"file": ("name.txt", b"content", "text/plain")} in the TestClient call |
Deployment Issues
| # | Problem | Solution |
|---|---|---|
| 36 | OpenAPI docs show wrong URLs behind reverse proxy | Set root_path in FastAPI() constructor or pass --root-path to uvicorn |
| 37 | Static files return 404 in production | Ensure the directory path in StaticFiles() is absolute or correctly relative to the working directory |
| 38 | Gunicorn workers crash with [CRITICAL] WORKER TIMEOUT |
Increase --timeout (default 30s) or investigate if an endpoint is blocking too long |
| 39 | Memory grows unbounded with streaming | Ensure you are using yield in streaming responses and not accumulating data in memory |
| 40 | Application does not shut down cleanly | Use the lifespan context manager to properly close database connections, HTTP clients, and other resources |
7. FAQ
Q1: What Python versions does FastAPI support?
FastAPI requires Python 3.8 or higher. Type hint features used by FastAPI (like str | None) require Python 3.10+; for older Python versions use Optional[str] from typing.
Q2: Is FastAPI async or sync?
Both. Endpoints declared with async def run on the async event loop. Endpoints declared with def run in an external thread pool automatically. You can mix both freely in the same application.
Q3: How does FastAPI compare to Flask in performance? FastAPI runs on ASGI (Starlette + Uvicorn), which supports async I/O natively. For I/O-bound workloads, FastAPI significantly outperforms Flask (WSGI). For CPU-bound work, both are limited by Python's GIL.
Q4: Can I use FastAPI without Pydantic models? Yes. You can use plain Python types for path, query, header, and cookie parameters. However, for request bodies, Pydantic models are the recommended approach. You can also use dataclasses.
Q5: How do I disable the docs endpoints in production?
Set openapi_url=None in the FastAPI() constructor. This disables /openapi.json, /docs, and /redoc entirely.
Q6: Can I use SQLAlchemy / SQLModel / Tortoise ORM / MongoDB with FastAPI? Yes. FastAPI is ORM-agnostic. Use dependency injection to provide database sessions. SQLModel is particularly well-integrated since it combines SQLAlchemy and Pydantic.
Q7: How do I handle CORS?
Add CORSMiddleware via app.add_middleware(). Specify allowed origins, methods, and headers. See section 5.5 for a complete example.
Q8: What is response_model vs return type annotation?
response_model is a decorator parameter that filters and validates the response. Return type annotations (e.g., -> ItemOut) also work for the OpenAPI schema. If both are provided, response_model takes priority.
Q9: How do I version my API?
Common approaches: (1) URL prefix versioning with APIRouter(prefix="/v1") or app.mount(), (2) header-based versioning with custom middleware, (3) separate FastAPI() instances mounted as sub-applications.
Q10: Can I serve HTML pages with FastAPI?
Yes. Use Jinja2Templates for server-rendered HTML and HTMLResponse as the response class. You can also mount StaticFiles for CSS, JS, and images.
Q11: How do I add request/response logging? Use a custom middleware function (see section 5.12). The middleware receives every request and can inspect both the request and response.
Q12: What is the difference between fastapi dev and fastapi run?
fastapi dev enables auto-reload and binds to 127.0.0.1 (localhost only). fastapi run disables auto-reload and binds to 0.0.0.0 (all interfaces). Use dev during development, run in production.
Q13: How do I handle file downloads?
Return a FileResponse with the path parameter pointing to the file. Set filename for the download name and media_type for the Content-Type header.
Q14: Can I use WebSockets and HTTP endpoints in the same app?
Yes. WebSocket endpoints use @app.websocket() and coexist with HTTP endpoints. They share the same dependency injection system.
Q15: How do I implement pagination?
Use query parameters (page, size or skip, limit) with Query() for validation. Return a response model that includes total, page, size, and items. See section 5.8.
Q16: What is Depends() vs Security()?
Depends() declares a dependency. Security() is identical but additionally accepts OAuth2 scopes and integrates with the OpenAPI security scheme display.
Q17: How do I run background tasks?
Declare a BackgroundTasks parameter in your endpoint and call .add_task(). Tasks run after the response is sent. For heavy work, use Celery or a similar task queue.
Q18: How do I add custom headers to responses?
Declare a Response parameter and set response.headers["X-Custom"] = "value". The response object is merged with the return value.
Q19: What is root_path and when do I need it?
root_path tells FastAPI that it is served under a URL prefix (e.g., /api/v1). Set this when deploying behind a reverse proxy that strips the prefix. It ensures OpenAPI docs and redirect URLs are correct.
Q20: Can I use dependency injection in WebSocket endpoints?
Yes. WebSocket endpoints support Depends() the same way HTTP endpoints do.
Q21: How do I validate query parameters as enums?
Define a Python Enum class and use it as the type annotation. FastAPI validates that the value is one of the allowed options and generates the enum in OpenAPI.
Q22: How do I return different status codes from the same endpoint?
Use Response parameter to set response.status_code dynamically, or raise HTTPException for error cases. Declare additional codes in responses={} for OpenAPI documentation.
Q23: What is separate_input_output_schemas?
When True (default), FastAPI generates separate OpenAPI schemas for request bodies and response bodies even if they use the same Pydantic model. This prevents clients from seeing read-only fields in request schemas.
Q24: What does strict_content_type=True do?
It enforces that the Content-Type header matches the expected media type. This provides CSRF protection by rejecting requests that claim to be JSON but are actually form-encoded (which browsers can send without CORS preflight).
Q25: How do I handle multiple file uploads with metadata?
Combine UploadFile parameters with Form() parameters in the same endpoint. Each gets its own parameter declaration.
Q26: Can I generate client SDKs from my FastAPI app?
Yes. FastAPI generates a full OpenAPI 3.1.0 schema at /openapi.json. Use any OpenAPI code generator (e.g., openapi-generator, swagger-codegen, openapi-typescript-codegen) to generate client libraries.
Q27: How do I test WebSocket endpoints?
Use TestClient with client.websocket_connect("/ws") as a context manager. Inside the context, call ws.send_text() and ws.receive_text() to simulate communication.
Q28: How do I handle large request bodies?
Uvicorn has no built-in body size limit. Add limits at the reverse proxy level (e.g., Nginx client_max_body_size) or implement a custom middleware that checks Content-Length.
Q29: What is an APIRouter and when should I use it?
APIRouter is FastAPI's mechanism for organizing endpoints into modules. Use it when your application has multiple resource types or feature areas. Include routers in the main app with app.include_router().
Q30: How do I use Server-Sent Events (SSE)?
Return an EventSourceResponse that yields ServerSentEvent objects. See section 4.14 for a complete example. Clients connect with the EventSource browser API or similar.
8. Quick Reference
Path Operation Decorator Parameters
| Parameter | Type | Purpose |
|---|---|---|
path |
str |
URL path (first positional argument) |
response_model |
type |
Pydantic model for response filtering/validation |
status_code |
int |
Default HTTP status code |
tags |
list[str] |
OpenAPI tags for grouping |
summary |
str |
Short endpoint summary |
description |
str |
Detailed endpoint description |
response_description |
str |
Description of the response |
responses |
dict |
Additional OpenAPI response definitions |
deprecated |
bool |
Mark as deprecated in OpenAPI |
operation_id |
str |
Custom OpenAPI operation ID |
include_in_schema |
bool |
Include in OpenAPI schema |
response_model_include |
set |
Fields to include in response |
response_model_exclude |
set |
Fields to exclude from response |
response_model_exclude_unset |
bool |
Exclude unset fields from response |
response_model_exclude_defaults |
bool |
Exclude default-valued fields |
response_model_exclude_none |
bool |
Exclude None-valued fields |
callbacks |
list |
OpenAPI callback definitions |
openapi_extra |
dict |
Additional OpenAPI metadata |
Parameter Types
| Function | Source | Example |
|---|---|---|
Path() |
URL path segment | /items/{item_id} |
Query() |
URL query string | ?skip=0&limit=10 |
Header() |
HTTP headers | Authorization: Bearer token |
Cookie() |
HTTP cookies | session_id=abc123 |
Body() |
Request body (JSON) | {"name": "Widget"} |
Form() |
Form-encoded body | username=admin&password=secret |
File() |
Multipart file upload | Binary file content |
Common Validation Parameters
These parameters work with Path(), Query(), Body(), Header(), Cookie(), Form():
| Parameter | Applies To | Purpose |
|---|---|---|
default |
All | Default value |
title |
All | Schema title for docs |
description |
All | Schema description for docs |
alias |
All | Alternative field name in the request |
min_length |
str |
Minimum string length |
max_length |
str |
Maximum string length |
pattern |
str |
Regex pattern |
ge |
numeric | Greater than or equal |
gt |
numeric | Greater than |
le |
numeric | Less than or equal |
lt |
numeric | Less than |
multiple_of |
numeric | Must be a multiple of this value |
examples |
All | Example values for OpenAPI docs |
deprecated |
All | Mark as deprecated |
include_in_schema |
All | Include in OpenAPI schema |
HTTP Status Code Constants
FastAPI re-exports Starlette's status codes via fastapi.status:
| Constant | Code | Typical Use |
|---|---|---|
HTTP_200_OK |
200 | Successful GET/PUT/PATCH |
HTTP_201_CREATED |
201 | Successful POST (resource created) |
HTTP_204_NO_CONTENT |
204 | Successful DELETE (no body) |
HTTP_301_MOVED_PERMANENTLY |
301 | Permanent redirect |
HTTP_302_FOUND |
302 | Temporary redirect |
HTTP_304_NOT_MODIFIED |
304 | Cache hit |
HTTP_400_BAD_REQUEST |
400 | Client error |
HTTP_401_UNAUTHORIZED |
401 | Authentication required |
HTTP_403_FORBIDDEN |
403 | Insufficient permissions |
HTTP_404_NOT_FOUND |
404 | Resource not found |
HTTP_405_METHOD_NOT_ALLOWED |
405 | Wrong HTTP method |
HTTP_409_CONFLICT |
409 | Resource conflict |
HTTP_422_UNPROCESSABLE_ENTITY |
422 | Validation error |
HTTP_429_TOO_MANY_REQUESTS |
429 | Rate limited |
HTTP_500_INTERNAL_SERVER_ERROR |
500 | Server error |
HTTP_502_BAD_GATEWAY |
502 | Upstream error |
HTTP_503_SERVICE_UNAVAILABLE |
503 | Service down |
Import Cheat Sheet
# Core
from fastapi import FastAPI, APIRouter, Request, Response
# Parameters
from fastapi import Path, Query, Header, Cookie, Body, Form, File, UploadFile
# Dependencies
from fastapi import Depends, Security
# Exceptions
from fastapi import HTTPException, WebSocketException
from fastapi.exceptions import RequestValidationError
# Responses
from fastapi.responses import (
JSONResponse, HTMLResponse, PlainTextResponse,
RedirectResponse, StreamingResponse, FileResponse,
ORJSONResponse, UJSONResponse,
EventSourceResponse,
)
# Security
from fastapi.security import (
OAuth2PasswordBearer, OAuth2PasswordRequestForm,
OAuth2AuthorizationCodeBearer,
HTTPBasic, HTTPBasicCredentials,
HTTPBearer, HTTPAuthorizationCredentials,
APIKeyHeader, APIKeyQuery, APIKeyCookie,
)
# Middleware
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
# Background tasks
from fastapi import BackgroundTasks
# WebSockets
from fastapi import WebSocket, WebSocketDisconnect
# Testing
from fastapi.testclient import TestClient
# Templates & Static
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
# Encoding
from fastapi.encoders import jsonable_encoder
# Status codes
from fastapi import status
Tip
For architecture details, deployment configuration, security internals, and performance tuning, see the FastAPI Admin Guide (Admin Guide section).