FastAPI Intermediate: Structure, Dependencies and Data
Dependency Injection
In this lesson you will
- Write and use a dependency function
- Use a yield dependency for resources that need cleanup
- Create reusable Annotated dependency types
Introduction
Many endpoints need the same things: a database session, the current user, pagination settings. Copy-pasting that code is error-prone. FastAPI's dependency injection lets you write it once, and have FastAPI call it and pass the result into your endpoint. You declare what you need, and the framework provides it.
A first dependency
A dependency is just a function. Declare it with Depends.
Python
from typing import Annotated
from fastapi import Depends
def pagination(offset: int = 0, limit: int = 10):
return {"offset": offset, "limit": min(limit, 100)}
@app.get("/books")
def list_books(page: Annotated[dict, Depends(pagination)]):
return pageFastAPI sees Depends(pagination), reads the parameters of pagination (offset and limit become query parameters), runs it and passes the returned dictionary as page. The same dependency can serve many endpoints, with the limit capped at 100 in one place.
Dependencies with cleanup
A database session must be closed after the request. A dependency that uses yield does this.
Python
from sqlmodel import Session
def get_session():
with Session(engine) as session:
yield session # given to the endpoint
# leaving the with block closes the session after the responseCode after yield runs once the request has finished. This is the standard pattern for sessions and connections.
Quick quiz
Why use a dependency with yield for a database session?
Reusable aliases with Annotated
Repeating Annotated[Session, Depends(get_session)] everywhere is noisy. Define an alias once:
Python
SessionDep = Annotated[Session, Depends(get_session)]
@router.get("/{book_id}")
def get_book(book_id: int, session: SessionDep):
...This is the pattern used in the project from the previous lesson, and it also helps editors understand types.
Dependencies can depend on dependencies
A dependency can itself use Depends. For example, current_user can depend on get_token, which reads a header. FastAPI works out the order and calls each one once per request.
Dependencies for rules
A dependency can raise an error to block a request, for instance when the user is not allowed. You can also attach dependencies to a whole router so they apply to every route:
Python
router = APIRouter(prefix="/admin", dependencies=[Depends(require_admin)])The security course lesson later uses this idea for authentication.
Overriding dependencies in tests
Because endpoints receive their dependencies instead of creating them, tests can replace them. In a test you can point get_session at an in-memory database:
Python
app.dependency_overrides[get_session] = override_sessionThe next lessons build on this: a database layer and tests. In the earlier practice run, calling the API with the overridden session left the real database empty (0 rows), which shows tests do not touch real data.
Common mistakes
- Calling the dependency yourself: write
Depends(pagination), notDepends(pagination()). - Doing heavy work in a dependency that runs on every request.
- Hiding too much magic: keep dependencies small and well named.
Try it yourself
Write a dependency pagination that caps limit at 50 and use it in two endpoints. Then write a dependency that raises 400 when an X-Api-Version header is missing, and attach it to one router.
Summary
- A dependency is a function that FastAPI calls for you, and Depends passes the result to your endpoint.
- Use yield for resources such as database sessions that need cleanup, and Annotated aliases for reuse.
- Dependencies can be nested, attached to routers and overridden in tests.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading