Short answer
Depends resolves dependencies (database connection, current user, settings) per request and passes them to the route, and supports nested dependencies and cleanup through yield. Use async def when everything inside is asynchronous, and def for blocking code because FastAPI runs it in a threadpool.
Explanation
A dependency's result is cached within a single request, so if several dependencies request it, it is computed once. A dependency that uses yield suits opening a database session and closing it after the response. The most dangerous mistake is calling a blocking library (such as requests or a synchronous ORM) inside async def, because it blocks the event loop and stalls every request. You can override dependencies in tests through app.dependency_overrides.
Code example
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def current_user(token: str = Depends(oauth2_scheme), db=Depends(get_db)) -> User:
user = verify_token(db, token)
if not user:
raise HTTPException(status_code=401)
return user
@app.get("/me")
def me(user: Annotated[User, Depends(current_user)]): # def: The ORM is synchronous
return {"email": user.email}Common mistakes
- Using requests or a synchronous ORM inside async def, so the server freezes under load.
- Creating a global database session shared between requests instead of a per-request dependency.