Claude Code FastAPI Dependency Injection Patterns Guide
FastAPI’s dependency injection system is one of its most powerful features, enabling clean architecture and testable code. When combined with Claude Code, you can rapidly implement robust dependency injection patterns that follow best practices. This guide walks you through practical examples and actionable patterns you can apply immediately to your Python APIs.
Understanding FastAPI Dependency Injection
FastAPI’s dependency injection system allows you to declare dependencies at the route level, and FastAPI automatically resolves them for each request. This pattern promotes loose coupling, improves testability, and makes your code more maintainable.
The foundation of FastAPI’s DI system is the Depends() function. It lets you define dependencies that FastAPI will execute before your route handler, automatically injecting the results into your function parameters.
Basic Dependency Injection Patterns
Simple Dependency with Depends
The most straightforward pattern is creating a reusable dependency function that returns a value used by your route handler:
from fastapi import FastAPI, Depends
app = FastAPI()
def get_database_url():
return "postgresql://localhost/mydb"
@app.get("/items")
async def read_items(db_url: str = Depends(get_database_url)):
return {"database": db_url}
This pattern separates configuration from your route logic. When testing, you can easily override get_database_url with a test database.
Class-Based Dependencies
For more complex dependencies, use classes. FastAPI will instantiate them and handle async properly:
from fastapi import Depends
class DatabaseConnection:
def __init__(self, config: dict):
self.config = config
self.connection = None
async def connect(self):
# Establish connection
self.connection = await self.config["pool"].acquire()
return self.connection
async def disconnect(self):
if self.connection:
await self.config["pool"].release(self.connection)
def get_db(config: dict) -> DatabaseConnection:
return DatabaseConnection(config)
@app.post("/users")
async def create_user(
db: DatabaseConnection = Depends(get_db),
user_data: dict = Body(...)
):
conn = await db.connect()
# Create user logic
Claude Code can help you scaffold these patterns quickly by understanding your project structure and generating appropriate class hierarchies.
Practical Dependency Patterns for Real Applications
Authentication Dependencies
One of the most common use cases is implementing authentication:
from fastapi import HTTPException, Security
from fastapi.security import HTTPBearer
security = HTTPBearer()
async def verify_token(token: str = Security(security)):
if not token:
raise HTTPException(status_code=403, detail="Missing token")
# Validate token (JWT, API key, etc.)
payload = await validate_jwt(token)
if not payload:
raise HTTPException(status_code=401, detail="Invalid token")
return payload
@app.get("/protected-resource")
async def get_protected(
user: dict = Depends(verify_token)
):
return {"user": user["sub"], "data": "sensitive info"}
Optional Dependencies with Default Values
Sometimes you need dependencies that are optional:
from typing import Optional
async def get_cache():
# Return cache client or None
return CacheClient() if CACHE_ENABLED else None
@app.get("/items")
async def get_items(
cache: Optional[CacheClient] = Depends(get_cache)
):
if cache:
cached = await cache.get("items")
if cached:
return cached
items = await fetch_items_from_db()
if cache:
await cache.set("items", items, ttl=300)
return items
Parameterized Dependencies
For dependencies that need configuration, use factories:
def create_rate_limiter(max_requests: int, window: int):
async def rate_limiter(request: Request):
client_id = request.client.host
key = f"rate:{client_id}"
count = await redis.get(key)
if count and int(count) >= max_requests:
raise HTTPException(429, "Too many requests")
await redis.incr(key)
await redis.expire(key, window)
return True
return rate_limiter
@app.post("/api/data")
async def create_data(
rate_limit: bool = Depends(create_rate_limiter(100, 60))
):
# Your endpoint logic
Advanced Patterns for Production Systems
DependencyOverrides for Testing
FastAPI provides a powerful testing mechanism through dependency overrides:
from fastapi.testclient import TestClient
from fastapi import Depends
# In your test file
def get_mock_db():
return MockDatabase()
app.dependency_overrides[get_database_url] = get_mock_db
client = TestClient(app)
response = client.get("/items")
assert response.status_code == 200
Nested Dependencies
Dependencies can depend on other dependencies, creating a clean hierarchy:
async def get_current_user(token: str = Depends(verify_token)):
return await fetch_user(token)
async def get_user_profile(user: dict = Depends(get_current_user)):
profile = await fetch_profile(user["id"])
return {"user": user, "profile": profile}
@app.get("/profile")
async def read_profile(
data: dict = Depends(get_user_profile)
):
return data
This pattern keeps each dependency focused on a single responsibility while composing complex behaviors.
Conditional Dependencies
Use dependency conditions for feature flags or environment-specific behavior:
def get_storage_backend():
if os.getenv("STORAGE") == "s3":
return S3Storage()
return LocalStorage()
@app.post("/upload")
async def upload_file(
storage = Depends(get_storage_backend)
):
# Upload using the appropriate backend
Actionable Best Practices
-
Keep dependencies focused: Each dependency should do one thing well. Don’t bundle multiple responsibilities in a single dependency.
-
Use async by default: Define your dependencies with
async defunless they perform synchronous blocking operations. -
Leverage type hints: FastAPI uses type hints to validate and convert data. Always include proper types.
-
Document dependencies: Add docstrings explaining what each dependency does and any side effects.
-
Test with overrides: Use FastAPI’s
dependency_overridesfor comprehensive testing without mocking internals. -
Handle cleanup properly: If your dependency allocates resources, consider using context managers or generator patterns for proper cleanup.
Conclusion
FastAPI’s dependency injection system provides a robust foundation for building scalable Python APIs. By using these patterns with Claude Code’s assistance, you can rapidly implement clean, testable, and maintainable code structures. Start with simple dependencies and gradually adopt more advanced patterns as your application grows in complexity.
Remember that the key to successful dependency injection is maintaining clear boundaries between components while keeping your code flexible for testing and future modifications.
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
- Claude Skill Dependency Injection Patterns — If you are interested in applying DI concepts at the Claude skill level (composing pdf, xlsx, tdd, and other skills into pipelines), see this companion guide.
Built by theluckystrike — More at zovo.one