Depends and dependency injection

The lifespan loaded the client. Now every route needs a clean way to get at it. You could reach into app.state directly, but that ties every route to the app object. Depends gives you a better answer: declare a function that returns the resource, and any route that lists it as a Depends parameter receives it automatically.

router.py
python
from fastapi import APIRouter, Depends, Request
from models import StoryRequest
from service import build_story_prompt

router = APIRouter(prefix="/bedtime-story", tags=["bedtime-story"])


def get_llm(request: Request):
    """Provider function: pulls the shared client off app.state."""
    return request.app.state.llm_provider


@router.post("/stream")
async def stream_story(
    body: StoryRequest,
    llm = Depends(get_llm),
):
    prompt = build_story_prompt(body)
    return {"ok": True, "model": llm.model}

get_llm is the provider function. Any route that lists Depends(get_llm) as an argument gets the shared client. Swap the provider at test time without touching the routes.

Matching exercise: Match each Depends concept to its job

Loading practice…

FastAPI has a dependency_overrides dict on the app. In tests, you register a fake provider for get_llm, and every route that depends on it gets the fake automatically. No mocks inside your business logic, no patching imports. The route code stays honest and the test stays small.