agentbench / data /tech_docs /fastapi_dependencies.md
Nomearod's picture
feat: Day 4 — corpus, ingest script, first 10 golden questions
a152b95
# Dependency Injection in FastAPI
FastAPI includes a built-in dependency injection system that allows you to share logic, enforce authentication, manage database connections, and more. Dependencies are declared using `Depends()` and are resolved automatically for each request.
## Basic Dependency
A dependency is any callable (function or class) that FastAPI calls before the route handler:
```python
from fastapi import FastAPI, Depends, Query
app = FastAPI()
async def common_parameters(
skip: int = Query(default=0, ge=0),
limit: int = Query(default=100, ge=1, le=1000),
):
return {"skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return {"params": commons}
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return {"params": commons}
```
Both `/items/` and `/users/` share the same pagination logic. The `common_parameters` function is called once per request, and its return value is injected into the `commons` parameter.
## Class-Based Dependencies
Classes work as dependencies because calling a class creates an instance (i.e., `MyClass()` is callable):
```python
class PaginationParams:
def __init__(
self,
skip: int = Query(default=0, ge=0),
limit: int = Query(default=100, ge=1, le=1000),
):
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(pagination: PaginationParams = Depends(PaginationParams)):
return {"skip": pagination.skip, "limit": pagination.limit}
```
FastAPI provides a shorthand: `Depends(PaginationParams)` can also be written as `Depends()` when the type annotation already specifies the class: `pagination: PaginationParams = Depends()`.
## Sub-Dependencies
Dependencies can depend on other dependencies, forming a chain that FastAPI resolves automatically:
```python
def query_extractor(q: str | None = None):
return q
def query_or_default(q: str = Depends(query_extractor)):
if not q:
return "default_query"
return q
@app.get("/items/")
async def read_items(query: str = Depends(query_or_default)):
return {"query": query}
```
FastAPI resolves the dependency tree from the leaves up. In this case, `query_extractor` runs first, then `query_or_default` receives its result. The maximum depth of the dependency chain is not explicitly limited, but in practice chains deeper than 10 levels indicate a design issue.
## Dependencies with Yield (Resource Management)
Use `yield` in a dependency to run setup code before and cleanup code after the route handler executes. This is ideal for managing database sessions, file handles, or locks:
```python
from sqlalchemy.orm import Session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/items/")
async def read_items(db: Session = Depends(get_db)):
items = db.query(Item).all()
return items
```
The code before `yield` runs before the handler, the yielded value is injected as the dependency, and the code after `yield` runs after the response is sent. The `finally` block ensures cleanup happens even if an exception occurs. FastAPI supports up to 32 yield dependencies per request by default.
## Global Dependencies
Apply dependencies to every route in the application by passing them to the `FastAPI` constructor:
```python
from fastapi import FastAPI, Depends, Header, HTTPException
async def verify_api_key(x_api_key: str = Header()):
if x_api_key != "secret-key-123":
raise HTTPException(status_code=403, detail="Invalid API key")
app = FastAPI(dependencies=[Depends(verify_api_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Widget"}]
```
Every route in this application requires a valid `X-Api-Key` header. You can also scope dependencies to a specific router using `APIRouter(dependencies=[...])`.
## Caching Behavior
By default, if the same dependency is used multiple times within a single request (e.g., both a route and a sub-dependency use `Depends(get_db)`), FastAPI caches the result and calls the dependency only once. To disable caching and force a fresh call each time, use `Depends(get_db, use_cache=False)`.