Python for JS Devs · FastAPI

Dependency injection

Depends() is FastAPI's answer to Express middleware — but composable, typed, testable, and self-documenting.

Lesson 14Time ~14 min Win Inject a DB session & a current userPrereq Lessons 05, 13

In Express you thread shared concerns (DB handle, auth, config) through middleware and req. FastAPI inverts it: a route declares what it needs as a parameter, and FastAPI provides it. That's dependency injection.

1 · A dependency is just a function

from fastapi import Depends

def pagination(skip: int = 0, limit: int = 20) -> dict:
    return {"skip": skip, "limit": limit}

@app.get("/items")
def list_items(page: dict = Depends(pagination)):
    return page   # {"skip": 0, "limit": 20}, from ?skip=&limit=

FastAPI runs pagination, validates its params from the request too, and injects the result. Dependencies can depend on dependencies — they compose into a tree.

2 · The yield pattern: setup + teardown

A dependency that yields gives a resource to the route, then runs cleanup after the response — the analogue of middleware that wraps next(). This is the way to provide a DB session:

def get_db():
    db = SessionLocal()
    try:
        yield db          # hand the session to the route
    finally:
        db.close()        # always cleanup after response

@app.get("/users/{uid}")
def get_user(uid: int, db = Depends(get_db)):
    return db.get(User, uid)

3 · Auth as a dependency (the real payoff)

from fastapi import Header, HTTPException

def current_user(authorization: str = Header()):
    user = verify_token(authorization)
    if user is None:
        raise HTTPException(status_code=401, detail="Not authenticated")
    return user

@app.get("/me")
def me(user = Depends(current_user)):
    return user
Why DI beats middleware A dependency is explicit (the route lists exactly what it needs), typed (shows up in /docs), reusable (share current_user across routes), and testable (override it with app.dependency_overrides in tests — no monkeypatching the request object). It also short-circuits with a clean 401 before your handler even runs.

🎯 Your win

Write the pagination dependency and inject it into a /items route; confirm ?skip=5&limit=2 flows through /docs. Then sketch a current_user dependency that raises 401 when a fake header is missing. Seeing shared logic injected — not copy-pasted — is the win.

Check yourself

Q1. In FastAPI, a dependency is fundamentally:
Any callable passed to Depends(...) — usually a plain function.
Q2. A dependency that uses yield lets you:
Code after yield (in finally) runs post-response — perfect for closing a DB session.
Q3. A key testing advantage of DI is that you can:
app.dependency_overrides swaps real deps (DB, auth) for fakes — no monkeypatching needed.
Primary source FastAPI — Dependencies. Read through "Dependencies with yield".
💬 Ask your teacher: how do I apply a dependency to a whole router (e.g. require auth on every route in a group)? Ask for the dependencies=[...] pattern.