Forem

Cover image for Advanced FastAPI Patterns: Building Production-Ready APIs with Python [2024 Guide]
Aarav Joshi
Aarav Joshi

Posted on

Advanced FastAPI Patterns: Building Production-Ready APIs with Python [2024 Guide]

As a best-selling author, I invite you to explore my books on Amazon. Don't forget to follow me on Medium and show your support. Thank you! Your support means the world!

FastAPI has revolutionized Python API development with its modern, async-first approach. I've spent considerable time working with FastAPI, and I'll share advanced patterns that have proven effective in production environments.

Dependency Injection

Dependency injection in FastAPI provides clean separation of concerns and efficient resource management. Here's a practical implementation of a database dependency:

from fastapi import Depends
from sqlalchemy.orm import Session
from contextlib import contextmanager

class Database:
    def __init__(self):
        self.session = None

    @contextmanager
    def get_session(self):
        session = Session()
        try:
            yield session
        finally:
            session.close()

db = Database()

async def get_db():
    with db.get_session() as session:
        yield session

@app.get("/users/{user_id}")
async def get_user(user_id: int, db: Session = Depends(get_db)):
    return db.query(User).filter(User.id == user_id).first()
Enter fullscreen mode Exit fullscreen mode

Response Caching

Implementing Redis caching significantly improves API performance. Here's a robust caching implementation:

from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
import pickle

class CustomRedisBackend(RedisBackend):
    async def get(self, key: str):
        value = await self.redis.get(key)
        if value:
            return pickle.loads(value)
        return None

    async def set(self, key: str, value: any, expire: int = None):
        value = pickle.dumps(value)
        await self.redis.set(key, value, expire)

@app.on_event("startup")
async def startup():
    redis = aioredis.Redis(host='localhost', port=6379)
    FastAPICache.init(CustomRedisBackend(redis), prefix="fastapi-cache:")

@app.get("/products/{product_id}")
@FastAPICache.cache(expire=300)
async def get_product(product_id: int):
    return {"product_id": product_id}
Enter fullscreen mode Exit fullscreen mode

Background Tasks

Managing long-running operations effectively is crucial. Here's a pattern for handling background tasks:

from fastapi import BackgroundTasks
from celery import Celery

celery_app = Celery('tasks', broker='redis://localhost:6379/0')

@celery_app.task
def process_video(video_id: int):
    # Video processing logic
    pass

@app.post("/videos/")
async def upload_video(
    video: UploadFile,
    background_tasks: BackgroundTasks
):
    video_id = save_video(video)
    background_tasks.add_task(process_video.delay, video_id)
    return {"status": "processing"}
Enter fullscreen mode Exit fullscreen mode

Rate Limiting

Protecting API resources requires effective rate limiting. Here's a Redis-based implementation:

from fastapi import HTTPException
import time

class RateLimiter:
    def __init__(self, redis_client, limit: int, window: int):
        self.redis = redis_client
        self.limit = limit
        self.window = window

    async def is_allowed(self, key: str) -> bool:
        current = int(time.time())
        window_start = current - self.window

        async with self.redis.pipeline() as pipe:
            pipe.zremrangebyscore(key, 0, window_start)
            pipe.zadd(key, {str(current): current})
            pipe.zcard(key)
            pipe.expire(key, self.window)
            results = await pipe.execute()

        return results[2] <= self.limit

@app.get("/api/resource")
async def get_resource(
    redis: Redis = Depends(get_redis),
    user: User = Depends(get_current_user)
):
    rate_limiter = RateLimiter(redis, limit=100, window=3600)
    if not await rate_limiter.is_allowed(f"rate_limit:{user.id}"):
        raise HTTPException(status_code=429, detail="Rate limit exceeded")
    return {"data": "resource"}
Enter fullscreen mode Exit fullscreen mode

Custom Middleware

Middleware provides powerful request/response modification capabilities:

from fastapi import Request
from fastapi.middleware.base import BaseHTTPMiddleware
import time

class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, 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

app.add_middleware(TimingMiddleware)
Enter fullscreen mode Exit fullscreen mode

API Versioning

Maintaining API versions is essential for backward compatibility:

from fastapi import APIRouter

v1_router = APIRouter(prefix="/v1")
v2_router = APIRouter(prefix="/v2")

@v1_router.get("/users/{user_id}")
async def get_user_v1(user_id: int):
    return {"version": "1", "user_id": user_id}

@v2_router.get("/users/{user_id}")
async def get_user_v2(user_id: int):
    return {"version": "2", "user_id": user_id}

app.include_router(v1_router)
app.include_router(v2_router)
Enter fullscreen mode Exit fullscreen mode

Error Handling

Consistent error handling improves API reliability:

from fastapi import HTTPException
from fastapi.responses import JSONResponse

class CustomException(Exception):
    def __init__(self, message: str, code: str):
        self.message = message
        self.code = code

@app.exception_handler(CustomException)
async def custom_exception_handler(request, exc):
    return JSONResponse(
        status_code=400,
        content={
            "error": {
                "code": exc.code,
                "message": exc.message
            }
        }
    )

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id < 0:
        raise CustomException(
            message="Invalid item ID",
            code="INVALID_ID"
        )
    return {"item_id": item_id}
Enter fullscreen mode Exit fullscreen mode

Testing

Comprehensive testing ensures API reliability:

from fastapi.testclient import TestClient
import pytest

client = TestClient(app)

@pytest.fixture
def test_db():
    # Setup test database
    db = Database()
    yield db
    # Cleanup

def test_read_item():
    response = client.get("/items/1")
    assert response.status_code == 200
    assert response.json() == {"item_id": 1}

def test_create_item():
    response = client.post(
        "/items/",
        json={"name": "Test Item"}
    )
    assert response.status_code == 201
Enter fullscreen mode Exit fullscreen mode

Production Deployment

For production deployment, consider these configurations:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn

app = FastAPI(
    title="Production API",
    description="Production-ready FastAPI application",
    version="1.0.0",
    docs_url="/documentation",
    redoc_url=None
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://allowed-domain.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=8000,
        workers=4,
        log_level="info",
        reload=False
    )
Enter fullscreen mode Exit fullscreen mode

These patterns form the foundation of robust FastAPI applications. The async capabilities, combined with proper resource management and error handling, create highly performant APIs. Regular testing and monitoring ensure reliability in production environments.

Remember to adapt these patterns based on specific requirements and scale. FastAPI's flexibility allows for customization while maintaining performance and code clarity.


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!

Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Top comments (0)