Learn / Frameworks / FastAPI / Dependency Injection and Auth

Intermediate 16 min

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 page

Dependencies 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)
Testing benefit

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}