Skip to content

FastAPI’s Depends system¶

🎯 What You’ll Learn¶

  • What dependency injection is and why it matters
  • How FastAPI’s Depends system works
  • How to use Depends to 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.
  • yield allows 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 call get_session() and inject the result into session
  • 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