FastAPI in Production: Best Practices and Performance Tips
FastAPI has become the go-to framework for building modern Python APIs, but taking it from development to production requires careful planning. Here’s your complete guide to production-ready FastAPI applications.
Project Structure for Scale
app/
├── api/
│ ├── v1/
│ │ ├── endpoints/
│ │ └── __init__.py
│ └── dependencies.py
├── core/
│ ├── config.py
│ ├── security.py
│ └── database.py
├── models/
├── schemas/
└── main.py
Configuration Management
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "My API"
database_url: str
secret_key: str
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
Database Connection Pooling
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
engine = create_async_engine(
settings.database_url,
pool_size=20,
max_overflow=0,
pool_pre_ping=True
)
AsyncSessionLocal = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
Security Best Practices
JWT Authentication with Refresh Tokens
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer
import jwt
security = HTTPBearer()
async def get_current_user(token: str = Depends(security)):
try:
payload = jwt.decode(token.credentials, settings.secret_key, algorithms=["HS256"])
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(status_code=401, detail="Invalid token")
return await get_user(user_id)
except jwt.PyJWTError:
raise HTTPException(status_code=401, detail="Invalid token")
Performance Optimizations
Response Caching
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://localhost")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
@app.get("/users/{user_id}")
@cache(expire=300)
async def get_user(user_id: int):
return await fetch_user(user_id)
Monitoring and Logging
import structlog
from fastapi import Request
import time
logger = structlog.get_logger()
@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
logger.info(
"request_completed",
method=request.method,
url=str(request.url),
status_code=response.status_code,
process_time=process_time
)
return response
Deployment with Docker
FROM python:3.11-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", "--workers", "4"]
Production FastAPI isn’t just about writing fast code—it’s about building resilient, secure, and maintainable systems that can handle real-world traffic and requirements.