FastAPI’s dependency injection system is one of its defining features, letting you elegantly manage request-scoped values, authentication, data access, and more. In many cases, dependencies are required, but sometimes you want to make them optional. Maybe your endpoint has different behavior depending on whether the client is authenticated, or perhaps you want to provide a sensible default if a dependency isn’t supplied.
In this article, I’ll walk through how to make dependencies optional in FastAPI, why you might want to do it, and show you a couple of practical patterns for leveraging optional dependencies—notably for flexible authentication.
Why Make Dependencies Optional?
Some common scenarios include:
- Authentication: Public endpoints that provide extra info if the user is authenticated, but work for anonymous users too.
- Feature Flags: Toggle functionality depending on an optional header or feature flag.
- Fallbacks: Attempt to fetch an object from a cache, otherwise proceed without it.
FastAPI’s dependency injection provides a neat way to handle these cases without a mess of manual request handling.
Implementation: Optional Dependencies with Depends
Let’s look at a basic example. Imagine you have a dependency that tries to extract a user from a token, but the endpoint should work either way:
from fastapi import FastAPI, Depends, Request, HTTPException
from typing import Optional
app = FastAPI()
async def get_current_user(request: Request) -> Optional[str]:
token = request.headers.get("authorization")
if not token:
return None # Not authenticated
# In practice, do token validation here.
user = "fasteddy" # Dummy user
return user
@app.get("/profile")
async def profile(user: Optional[str] = Depends(get_current_user)):
if user:
return {"message": f"Welcome back, {user}!"}
return {"message": "Hello, anonymous visitor!"}
- If a client sends an Authorization header, the endpoint personalizes the message. Otherwise, it uses a generic greeting.
- Because we type-annotated the dependency return as
Optional[str]
and default to None, FastAPI knows not to throw a 422 error if the user isn’t resolved.
Advanced Pattern: Non-Raising Dependencies
If your dependency might raise exceptions (e.g., for malformed headers), but you’d prefer it simply be None
if anything goes wrong, you can use a wrapper:
from fastapi import Depends
def optional_dep(dep_fn):
async def wrapper(*args, **kwargs):
try:
return await dep_fn(*args, **kwargs)
except Exception:
return None
return wrapper
@app.get("/dashboard")
async def dashboard(user: Optional[str] = Depends(optional_dep(get_current_user))):
# ...
When Not to Use Optional Dependencies
- Security: Avoid making authentication optional unless intentionally designing for public endpoints.
- Data access: If your dependency is crucial for business logic, keep it required!
Conclusion
Optional dependencies open up a ton of flexibility for API design in FastAPI. Whether you’re offering enhanced user experiences to authenticated users, implementing feature flags, or just making your endpoints more robust, you can handle it cleanly with FastAPI’s dependency injection.
As always, happy hacking!
— Fast Eddy
Leave a Reply