Skip to content

09 Dependency Injection

DodaTech 4 min read

title: Dependency Injection in FastAPI REST APIs weight: 19 date: 2026-06-28 lastmod: 2026-06-28 description: Master FastAPI dependency injection with Depends, reusable dependencies for auth, database sessions, pagination, and dependency hierarchies for clean architecture. tags: [api-development, fastapi]


FastAPI dependency injection uses the Depends function to declare dependencies in path operation parameters, automatically resolving reusable components like authentication, database sessions, pagination, and configuration.

```mermaid
flowchart TD
  A[Route Handler] --> B[Dependency 1]
  A --> C[Dependency 2]
  B --> D[Auth Check]
  C --> E[Database Session]
  D --> F[User Object]
  E --> G[DB Connection]
  A --> H[Response]
  style A fill:#e1f5fe
  style B fill:#fff9c4
  style C fill:#fff9c4
  style F fill:#c8e6c9
  style G fill:#c8e6c9

Dependencies are callable functions that return values. FastAPI calls them automatically and injects the results into the route handler. Dependencies can have their own dependencies (nesting). Cacheing means dependencies are called once per request even if used multiple times.

Think of dependency injection like a power strip. Each appliance (route handler) plugs into the strip and gets electricity (database connection, auth info). The power strip manages the flow. If two appliances need electricity, they share from the same source.

Example: Basic Dependencies

from fastapi import FastAPI, Depends, Header, HTTPException
from typing import Optional

app = FastAPI()

# Simple dependency
def get_pagination(page: int = 1, limit: int = 10):
    return {"page": page, "limit": limit, "skip": (page - 1) * limit}

# Dependency with validation
def verify_token(authorization: Optional[str] = Header(None)):
    if not authorization:
        raise HTTPException(status_code=401, detail="No auth header")
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid auth format")
    token = authorization.replace("Bearer ", "")
    if token != "valid-token":
        raise HTTPException(status_code=401, detail="Invalid token")
    return {"user_id": 42, "role": "admin"}

@app.get("/api/items")
def list_items(
    pagination: dict = Depends(get_pagination),
    user: dict = Depends(verify_token)
):
    return {
        "user": user,
        "pagination": pagination,
        "items": ["item1", "item2"]
    }

Example: Database Session Dependency

from fastapi import FastAPI, Depends
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session

DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# Dependency that provides database session
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/api/users")
def list_users(db: Session = Depends(get_db)):
    users = db.query(UserModel).all()
    return {"users": users}

@app.post("/api/users", status_code=201)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
    db_user = UserModel(**user.model_dump())
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

Example: Dependency with Sub-dependencies

from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel

app = FastAPI()

class PaginationParams(BaseModel):
    page: int = 1
    limit: int = 10

# Sub-dependency
def get_pagination(page: int = 1, limit: int = 10):
    return PaginationParams(page=page, limit=limit)

# Reusable auth dependency
async def get_current_user(token: str = Depends(verify_token)):
    return token

# Auth with role check
def require_admin(user: dict = Depends(get_current_user)):
    if user["role"] != "admin":
        raise HTTPException(status_code=403, detail="Admin access required")
    return user

# Composite dependency
async def get_filtered_query(
    db: Session = Depends(get_db),
    pagination: PaginationParams = Depends(get_pagination),
    user: dict = Depends(get_current_user)
):
    return {
        "db": db,
        "page": pagination.page,
        "limit": pagination.limit,
        "user_id": user["user_id"]
    }

@app.get("/api/admin/users")
def admin_list_users(ctx: dict = Depends(get_filtered_query)):
    # ctx has db, page, limit, user_id
    return {"context": ctx}

Common Mistakes

  1. Not using Depends() — Declaring a dependency as a parameter type without Depends() treats it as a query parameter, not a dependency.
  2. Creating new instances for each request — Dependencies that create database connections should use yield to clean up. Resource leaks cause server crashes.
  3. Mixing sync and async dependencies incorrectly — FastAPI handles both, but async dependencies can only use async sub-dependencies.
  4. Not caching dependencies — FastAPI's Depends caches results per request. Repeated Depends(same_func) calls the dependency once. Use this rather than storing results manually.
  5. Overcomplicating dependency chains — Deeply nested dependencies with side effects are hard to debug. Keep dependencies simple and focused.

Practice Questions

  1. What is the purpose of Depends() in FastAPI?
  2. How do you create a dependency that cleans up resources after use?
  3. Can dependencies have sub-dependencies?
  4. How does FastAPI cache dependency results per request?
  5. Challenge: Build a dependency injection system for a blog API with dependencies for: authentication (JWT verify), authorization (check role), database session, pagination, and query filters. Each endpoint should compose the dependencies it needs.

FAQ

What is the difference between Depends and BackgroundTasks?

Depends injects values into route handlers. BackgroundTasks runs functions after the response is sent. They serve different purposes.

Can dependencies be classes?

Yes, define a class with init or call and use Depends(MyClass). FastAPI handles dependency lifecycle for classes.

How do I make a dependency optional?

Use Optional[Type] = Depends(None). The dependency is skipped if it is None.

What happens if a dependency raises an exception?

FastAPI catches the exception and returns the appropriate error response. The route handler is not called.

Can I use global dependencies for all routes?

Yes, add dependencies to the app or router: app = FastAPI(dependencies=[Depends(verify_api_key)]). This applies to all routes.

Mini Project

Create a dependency injection system for a payment API. Include dependencies for: authentication (API key validation), merchant verification (find merchant by API key), request signing (verify HMAC signature), idempotency (check idempotency key), pagination, and database session. Wire them together in payment endpoints.

What's Next

Now learn about middleware in Building REST APIs with FastAPI.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro