Dependency Injection and Auth
Share logic with Depends, and protect routes with tokens.
What you will learn
- Write a dependency
- Inject a database session
- Protect a route
Many endpoints need the same things: a database session, the current user, pagination settings. Dependency injection lets you declare what an endpoint needs, and FastAPI provides it. You write the shared logic once with Depends.
from fastapi import Depends
def pagination(skip: int = 0, limit: int = 20):
return {"skip": skip, "limit": min(limit, 100)}
@app.get("/items")
def list_items(page: dict = Depends(pagination)):
return page
@app.get("/users")
def list_users(page: dict = Depends(pagination)):
return pageDependencies with cleanup
A dependency that uses yield runs setup before the endpoint and cleanup after it, perfect for database sessions.
from sqlalchemy.orm import Session
from .database import SessionLocal
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/posts")
def posts(db: Session = Depends(get_db)):
return db.query(Post).all()Authentication with a bearer token
FastAPI includes security helpers. The pattern: a dependency reads and verifies the token, and any route that lists it is protected.
from fastapi.security import OAuth2PasswordBearer
import jwt # pip install pyjwt
oauth2 = OAuth2PasswordBearer(tokenUrl="login")
SECRET, ALGO = "change-me", "HS256" # load from environment in real apps
def current_user(token: str = Depends(oauth2)):
try:
payload = jwt.decode(token, SECRET, algorithms=[ALGO])
except jwt.PyJWTError:
raise HTTPException(401, "Invalid token", headers={"WWW-Authenticate": "Bearer"})
return payload["sub"]
@app.get("/me")
def me(user: str = Depends(current_user)):
return {"user": user}Issuing tokens on login uses jwt.encode({"sub": username, "exp": ...}, SECRET, algorithm=ALGO). Passwords must be stored hashed, for example with passlib's bcrypt or argon2, never in plain text.
from fastapi import APIRouter
admin = APIRouter(prefix="/admin", dependencies=[Depends(current_user)])
@admin.get("/stats")
def stats():
return {"users": 10}
app.include_router(admin)Because dependencies are injected, tests can replace them: app.dependency_overrides[get_db] = fake_db.
Try it yourself
Write a dependency require_api_key that reads the X-API-Key header and raises 403 unless it equals "secret", and protect one route with it.
Show solution
from fastapi import Header
def require_api_key(x_api_key: str = Header(...)):
if x_api_key != "secret":
raise HTTPException(403, "Bad API key")
@app.get("/private", dependencies=[Depends(require_api_key)])
def private():
return {"ok": True}