FastAPI’s Depends system¶
🎯 What You’ll Learn¶
- What dependency injection is and why it matters
- How FastAPI’s
Dependssystem works - How to use
Dependsto inject database sessions, services, and shared logic - How to structure reusable, composable dependencies
🧠What Is Dependency Injection?¶
Dependency Injection (DI) is a design pattern where components (like routers or services) receive their dependencies (like database sessions or config objects) from an external source rather than creating them internally.
Benefits:
- ✅ Decouples logic from infrastructure
- ✅ Improves testability
- ✅ Enables reuse and composition
- ✅ Makes your code cleaner and more maintainable
FastAPI implements DI using the Depends() function.
🔧 Basic Example: Injecting a Database Session¶
Let’s revisit your get_session() function from earlier:
📄 db.py
from sqlmodel import Session, create_engine
sqlite_url = "sqlite:///./tasks.db"
engine = create_engine(sqlite_url, echo=True)
def get_session():
with Session(engine) as session:
yield session
- This is a generator-based dependency: FastAPI will automatically handle the lifecycle of the session.
yieldallows FastAPI to clean up resources after the request finishes.
🧩 Using Depends in a Route¶
📄 routers/tasks.py
from fastapi import Depends
from sqlmodel import Session
from db import get_session
@router.get("/")
def get_all_tasks(session: Session = Depends(get_session)):
tasks = session.exec(select(Task)).all()
return tasks
Depends(get_session)tells FastAPI to callget_session()and inject the result intosession- You don’t need to manually manage sessions — FastAPI does it for you
🧱 Composing Dependencies¶
You can nest dependencies to build reusable logic.
📄 services/task_service.py
from sqlmodel import Session, select
from models.task import Task
def get_task_by_id(task_id: int, session: Session) -> Task:
task = session.get(Task, task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
📄 routers/tasks.py
from services.task_service import get_task_by_id
@router.get("/{task_id}")
def read_task(task_id: int, session: Session = Depends(get_session)):
task = get_task_by_id(task_id, session)
return task
- Business logic lives in
services/ - Routes stay clean and focused on HTTP concerns
🧠Advanced: Parameterized Dependencies¶
You can also use dependencies that accept parameters:
def verify_token(token: str = Header(...)):
if token != "expected-token":
raise HTTPException(status_code=403, detail="Invalid token")
Then inject it:
@router.get("/secure", dependencies=[Depends(verify_token)])
def secure_endpoint():
return {"message": "Access granted"}
📚 How It Appears in Swagger UI¶
- Dependencies are automatically documented
- Required headers, query params, and bodies from dependencies show up in
/docs - You can test endpoints with dependencies interactively
🧠Recap¶
FastAPI’s Depends system lets you:
- Inject shared resources like DB sessions
- Compose business logic into reusable services
- Keep routes clean and declarative
- Automatically document dependencies
🧪 Practice Challenge¶
Create a dependency:
get_current_user()that returns a mock user object- Inject it into a route like
GET /me - Use
Depends(get_current_user)and return the user info