FastAPI Depends tells the framework how to resolve a callable for each request, including its own parameters and dependencies, then inject the result into the route.
It is most useful for request-scoped concerns such as authentication, database sessions, configuration, and repeated validation. It becomes confusing when every ordinary function call is hidden behind the dependency graph.
Table of contents
- Declare a dependency with Annotated
- Compose authentication and authorization
- Manage database sessions with yield
- Override dependencies in tests
- Avoid dependency graph abuse
- How this fits the rest of the stack
- FAQ
Declare a dependency with Annotated
from typing import Annotated
from fastapi import Depends, FastAPI, Query
app = FastAPI()
def paging(limit: int = Query(20, ge=1, le=100)) -> int:
return limit
@app.get('/items')
def list_items(limit: Annotated[int, Depends(paging)]):
return {'limit': limit}
Pass the dependency callable, not the result of calling it. FastAPI inspects its signature, resolves parameters, and caches a dependency result within the request by default. Annotated preserves the actual type for editors and static tools while carrying the dependency metadata.
Compose authentication and authorization
Keep credential extraction separate from policy. One dependency can parse and verify a token, another can require a role or resource relationship, and routes can request the resulting principal. Return a typed user object rather than a loose dictionary so downstream code has a clear contract.
Authentication dependencies should fail with deliberate HTTP responses and avoid logging raw credentials. Authorization still belongs close to the protected operation; a generic logged-in dependency does not prove access to a particular project.
Manage database sessions with yield
def get_session():
session = SessionLocal()
try:
yield session
finally:
session.close()
SessionDep = Annotated[Session, Depends(get_session)]
A yield dependency creates a resource and guarantees cleanup after the request. Decide transaction ownership explicitly: a route or service should commit intentionally, while the dependency handles close and rollback policy. Hiding automatic commits in cleanup makes failures and partial work difficult to reason about.
Override dependencies in tests
FastAPI’s dependency_overrides mapping can replace external services, users, clocks, or sessions during a test. Restore overrides after each test so state does not leak. Prefer small fakes with the same protocol over mocks that know every internal call.
app.dependency_overrides[get_current_user] = fake_user
try:
response = client.get('/profile')
finally:
app.dependency_overrides.clear()
A route that can be tested only by constructing the whole production graph has too many responsibilities. Put business rules in ordinary functions or classes and use dependencies to assemble request boundaries.
Avoid dependency graph abuse
- Use dependencies for request-scoped cross-cutting concerns
- Keep business logic callable outside FastAPI
- Prefer typed aliases for repeated declarations
- Avoid mutable global singleton state
- Document caching and cleanup behavior
- Test authorization failure and cleanup paths
Depends is not a service locator. If a dependency exists only to hide where an object comes from, explicit construction may be clearer. The best graph makes route requirements visible at the signature and keeps the underlying application usable without an HTTP request.
One additional design test is to trace cancellation and failure through the graph. If a client disconnects while a dependency is waiting on a database or remote API, the underlying library must release its connection and the yield cleanup must still run. Measure dependency latency separately from route logic so a slow authentication provider does not look like a slow query. For expensive read-only dependencies, request-local caching is useful; global caching needs explicit expiry and concurrency control. Keep startup resources in the application lifespan rather than constructing a new pool on every request. Finally, document whether each dependency returns a shared client, a request-scoped session, or an immutable value. That lifecycle is part of the interface even when Python’s type annotation cannot express it. Review the graph from the route inward: identity, authorization, database, external clients, and cleanup should each have one owner. When two dependencies both commit, close, or translate the same error, their hidden interaction becomes the next production bug.
How this fits the rest of the stack
A FastAPI dependency graph still needs runtime, secrets, database connections, and logs. The RunxBuild hosting calculator models those pieces together, and the RunxBuild dashboard keeps deployed configuration and runtime output visible.
Useful related references:
- Render 部署 FastAPI 项目: The Deployment Checklist That Actually Matters
- FastAPI Logging That Survives Production: A Working Developer’s Guide
- Django vs FastAPI: An Honest 2026 Comparison for Backend Teams
- Services on RunxBuild
FAQ
Should I call the function inside Depends?
No. Pass the callable, such as Depends(get_user), so FastAPI can resolve its parameters.
Why use Annotated?
It keeps the real parameter type visible while attaching FastAPI dependency metadata and supports reusable typed aliases.
Are dependency results cached?
By default FastAPI reuses a dependency result within one request. Configure use_cache only when repeated execution is truly required.
How do I close a database session?
Use a yield dependency with cleanup in finally, and define transaction commit and rollback ownership explicitly.
Can I override dependencies in tests?
Yes. Use app.dependency_overrides and clear the overrides after the test.