FastAPI Global Dependencies
Introduction
In FastAPI, dependencies are a powerful feature that enables code reuse, separation of concerns, and efficient implementation of common functionalities like authentication, database connections, or validation. While we've explored how to apply dependencies to specific endpoints, FastAPI also allows you to apply dependencies globally across your entire application.
Global dependencies execute for every request to your API, making them perfect for implementing consistent behaviors like:
- Authentication checks
- Request logging
- Performance monitoring
- User tracking
- Cross-cutting validations
In this tutorial, we'll explore how to set up and use global dependencies in FastAPI applications, and we'll discuss common use cases and best practices.
Understanding Global Dependencies
Global dependencies in FastAPI are dependencies that run for every request to your application. They're useful when you need to apply the same logic or validation across multiple routes without repeating yourself.
How Global Dependencies Differ from Regular Dependencies
Regular Dependencies | Global Dependencies |
---|---|
Applied to specific endpoints | Applied to all endpoints |
Defined in the route decorator | Defined when creating the FastAPI app |
Optional for specific routes | Executed for every request |
Setting Up Global Dependencies
Basic Structure
To add global dependencies to your FastAPI application, use the dependencies
parameter when creating your FastAPI app instance:
from fastapi import Depends, FastAPI
async def verify_token(x_token: str = Header(...)):
if x_token != "valid_token":
raise HTTPException(status_code=401, detail="Invalid X-Token")
return x_token
app = FastAPI(dependencies=[Depends(verify_token)])
@app.get("/items/")
async def read_items():
return {"items": "This endpoint is protected by a global dependency"}
In this example, verify_token
will run for every request to any endpoint in your application.
Use Cases for Global Dependencies
1. Authentication and Authorization
One of the most common use cases for global dependencies is implementing authentication:
from fastapi import Depends, FastAPI, Header, HTTPException
from typing import Optional
async def verify_api_key(api_key: str = Header(..., convert_underscores=False)):
if api_key != "your-secret-api-key":
raise HTTPException(status_code=403, detail="Invalid API Key")
return api_key
app = FastAPI(dependencies=[Depends(verify_api_key)])
@app.get("/users/")
async def get_users():
# This endpoint is protected by the global dependency
return {"message": "List of users"}
@app.get("/items/")
async def get_items():
# This endpoint is also protected by the global dependency
return {"message": "List of items"}
2. Request Logging
Global dependencies are excellent for implementing request logging:
import time
from fastapi import Depends, FastAPI, Request
async def log_request(request: Request):
start_time = time.time()
# Store the start time in the request state
request.state.start_time = start_time
print(f"Request started: {request.method} {request.url}")
# This will be executed after the request is processed
yield
process_time = time.time() - start_time
print(f"Request completed: {request.method} {request.url} took {process_time:.4f} seconds")
app = FastAPI(dependencies=[Depends(log_request)])
@app.get("/slow-operation/")
async def slow_operation():
# Simulate a slow operation
time.sleep(2)
return {"message": "Operation complete"}
3. Database Session Management
You can use global dependencies to set up and tear down database connections:
from fastapi import Depends, FastAPI
from sqlalchemy.orm import Session
# Database setup code omitted for brevity
# Assume we have engine and SessionLocal defined
async def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
app = FastAPI(dependencies=[Depends(get_db)])
@app.get("/users/")
async def get_users(db: Session = Depends(get_db)):
# Use the db session
users = db.query(User).all()
return users
Excluding Routes from Global Dependencies
Sometimes you might want certain routes (like health checks or public endpoints) to be exempt from global dependencies. FastAPI allows you to create separate sub-applications for this purpose:
from fastapi import Depends, FastAPI, Header, HTTPException
async def verify_token(x_token: str = Header(...)):
if x_token != "valid_token":
raise HTTPException(status_code=401, detail="Invalid X-Token")
return x_token
app = FastAPI()
secured_app = FastAPI(dependencies=[Depends(verify_token)])
# Mount the secured app under /secured path
app.mount("/secured", secured_app)
# This endpoint is not protected
@app.get("/health/")
async def health_check():
return {"status": "healthy"}
# This endpoint is protected
@secured_app.get("/items/")
async def read_items():
return {"items": "This endpoint is protected"}
Advanced Global Dependencies
Dependency with Sub-Dependencies
Global dependencies can have their own dependencies:
from fastapi import Depends, FastAPI, Header, HTTPException
async def get_user_from_token(token: str = Header(...)):
# In a real scenario, you'd verify the token and fetch user info
if token != "valid_token":
raise HTTPException(status_code=401, detail="Invalid token")
return {"user_id": 123, "username": "johndoe"}
async def verify_admin(user: dict = Depends(get_user_from_token)):
if user.get("username") != "admin":
raise HTTPException(status_code=403, detail="Admin privileges required")
return user
app = FastAPI(dependencies=[Depends(verify_admin)])
Adding Multiple Global Dependencies
You can add multiple global dependencies to your application:
from fastapi import Depends, FastAPI
async def dep1():
print("Running dependency 1")
async def dep2():
print("Running dependency 2")
async def dep3():
print("Running dependency 3")
app = FastAPI(dependencies=[Depends(dep1), Depends(dep2), Depends(dep3)])
Real-World Example: Complete API with Global Dependencies
Here's a more comprehensive example demonstrating global dependencies in a real-world scenario:
from fastapi import Depends, FastAPI, Header, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
import time
from typing import Optional
import jwt
from pydantic import BaseModel
# Models
class User(BaseModel):
id: int
username: str
role: str
# Auth dependency
async def get_current_user(authorization: str = Header(default=None)):
if not authorization:
raise HTTPException(status_code=401, detail="Authorization header missing")
try:
scheme, token = authorization.split()
if scheme.lower() != "bearer":
raise HTTPException(status_code=401, detail="Invalid authentication scheme")
# In production, use proper JWT verification
payload = jwt.decode(token, "secret_key", algorithms=["HS256"])
user = User(
id=payload["user_id"],
username=payload["username"],
role=payload["role"]
)
except Exception as e:
raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}")
return user
# Logging dependency
async def log_request(request: Request):
request.state.start_time = time.time()
request_id = f"req-{time.time()}"
print(f"[{request_id}] Request started: {request.method} {request.url}")
try:
# Process the request
yield
# Log after request is processed
process_time = time.time() - request.state.start_time
print(f"[{request_id}] Request completed: {process_time:.4f}s")
except Exception as e:
# Log exceptions
process_time = time.time() - request.state.start_time
print(f"[{request_id}] Request failed after {process_time:.4f}s: {str(e)}")
raise
# Create main app without global dependencies
app = FastAPI(title="My API with Global Dependencies")
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Create protected API with global dependencies
secured_app = FastAPI(dependencies=[Depends(get_current_user), Depends(log_request)])
# Mount secured app
app.mount("/api", secured_app)
# Public routes
@app.get("/")
async def root():
return {"message": "Welcome to the API"}
@app.get("/health")
async def health_check():
return {"status": "healthy"}
# Protected routes
@secured_app.get("/users/me")
async def get_me(current_user: User = Depends(get_current_user)):
return current_user
@secured_app.get("/admin")
async def admin_route(current_user: User = Depends(get_current_user)):
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Admin privileges required")
return {"message": "Welcome to admin section"}
Best Practices for Global Dependencies
- Keep them lightweight: Global dependencies run for every request, so keep them efficient.
- Handle errors appropriately: Provide clear error messages when dependencies fail.
- Use yield for cleanup: When resources need cleanup (like database sessions), use
yield
for proper cleanup. - Separate public and secured APIs: Use separate FastAPI instances for public and secured endpoints.
- Consider performance: Monitor how global dependencies affect your API's performance.
Summary
Global dependencies in FastAPI provide a clean way to implement functionality that should run for every request to your application. They're particularly useful for:
- Authentication and authorization
- Request logging and monitoring
- Database session management
- Input validation
- Cross-cutting concerns
By structuring your global dependencies carefully, you can maintain clean, DRY code while ensuring consistent behavior across your API.
Additional Resources
- FastAPI Official Documentation on Dependencies
- FastAPI Documentation on Global Dependencies
- Security and Authentication in FastAPI
Exercises
- Create a global dependency that tracks and limits the number of requests from a specific IP address.
- Implement a global dependency that validates a custom header parameter across all endpoints.
- Create an API with both secured and public routes using the mounting technique described in this tutorial.
- Implement a global dependency for performance monitoring that logs slow requests (taking over 1 second).
- Design a role-based access control system using global dependencies with different permission levels.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)