Home Python FastAPI Dependency Injection — How and Why to Use It

FastAPI Dependency Injection — How and Why to Use It

⚡ Quick Answer
FastAPI's Depends() is a built-in mechanism for sharing logic across endpoints. It allows you to declare functions that FastAPI executes before your route logic runs. The framework handles 'Inversion of Control'—it parses the dependency's requirements (like headers or database connections), executes the code, and injects the result into your endpoint as an argument. This is essential for clean code, automated testing, and enforcing global security rules without repetitive boilerplate.

Basic Dependency — Shared Query Parameters

A dependency is just a standard Python function. By using Depends(pagination), you tell FastAPI to treat the arguments of the pagination function as if they were part of the endpoint itself. This is the cleanest way to standardize pagination, filtering, or sorting logic across your entire API.

io/thecodeforge/dependencies/pagination.py · PYTHON
123456789101112131415161718
from fastapi import FastAPI, Depends
from typing import Annotated

app = FastAPI()

# Standardized pagination logic for all Forge APIs
def common_params(skip: int = 0, limit: int = 100):
    return {"skip": skip, "limit": limit}

@app.get('/users')
def list_users(params: Annotated[dict, Depends(common_params)]):
    # Business logic to fetch users using skip/limit
    return {"context": "users", "data": params}

@app.get('/orders')
def list_orders(params: Annotated[dict, Depends(common_params)]):
    # Reusing the same logic without duplication
    return {"context": "orders", "data": params}
▶ Output
GET /users?skip=20&limit=10 -> {"context": "users", "data": {"skip": 20, "limit": 10}}

Authentication Dependency & Logic Branching

Dependencies are the gatekeepers of your routes. By raising an HTTPException inside a dependency, you stop the request flow before it ever reaches your sensitive logic. This ensures that unauthorized users are rejected early and consistently.

io/thecodeforge/security/auth.py · PYTHON
1234567891011121314151617
from fastapi import FastAPI, Depends, HTTPException, Header, status
from typing import Annotated

app = FastAPI()

def validate_api_key(x_forge_token: Annotated[str | None, Header()] = None):
    # In production, check against HashiCorp Vault or a Secrets DB
    if x_forge_token != 'forge-prod-secret':
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, 
            detail='Invalid Forge API Key'
        )
    return {"user": "admin_service", "access": "full"}

@app.get('/secure-data')
def get_internal_metrics(auth: Annotated[dict, Depends(validate_api_key)]):
    return {"metrics": "[REDACTED]", "caller": auth['user']}
▶ Output
401 Unauthorized if header X-Forge-Token is missing or incorrect.

The 'Yield' Pattern: Database Session Management

Managing database connections is error-prone. You must open the session, handle the request, and—crucially—ensure the connection is returned to the pool even if a crash occurs. FastAPI's yield dependencies act like a context manager, automating the teardown logic so you never leak connections.

io/thecodeforge/database/session.py · PYTHON
123456789101112131415161718192021
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session

SQLALCHEMY_DATABASE_URL = "sqlite:///./forge_app.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def get_db():
    db = SessionLocal()
    try:
        # Code before yield is the 'Setup' phase
        yield db
    finally:
        # Code after yield is the 'Teardown' phase (Always runs)
        db.close()

@app.get('/db-status')
def check_db(db: Annotated[Session, Depends(get_db)]):
    # Use the session safely injected by FastAPI
    return {"status": "connected", "session_id": id(db)}
▶ Output
Database session opened before route, closed after response.

🎯 Key Takeaways

  • Depends() is the primary tool for Inversion of Control (IoC) in FastAPI, decoupling infrastructure from business logic.
  • Dependency Caching: By default, if multiple dependencies in the same request require the same sub-dependency, FastAPI reuses the first result to optimize performance.
  • The 'Yield' syntax provides a built-in 'try...finally' block for resources like database sessions, file handles, or network sockets.
  • Sub-dependencies: You can nest dependencies to create complex validation graphs (e.g., Auth -> Role Check -> Database Lookup).
  • Global and Router Dependencies: You can enforce dependencies on entire groups of routes using APIRouter(dependencies=[Depends(...)]).

Interview Questions on This Topic

  • QDescribe the 'Dependency Graph' resolution in FastAPI. How does it handle a scenario where Endpoint A depends on B and C, while both B and C depend on D?
  • QHow does FastAPI ensure that a 'yield' dependency cleans up resources if an unhandled exception occurs inside the route function?
  • QScenario: You need to implement a 'Soft-Delete' filter globally. How would you use a Router-level dependency to ensure every query in a specific module excludes deleted records?
  • QWhat are 'Security Dependencies' in FastAPI, and how do they integrate with the auto-generated Swagger (OpenAPI) documentation?
  • QHow would you override a dependency during an integration test to avoid hitting a real database? (Hint: app.dependency_overrides).

Frequently Asked Questions

What is the difference between Depends() and a regular function call?

A regular function call requires you to provide all arguments manually. With Depends(), FastAPI takes over: it inspects the dependency's own parameters, resolves them (even if they are other dependencies), and caches the result for the duration of the request. This allows for 'Recursive Dependency Resolution' which is impossible with standard function calls.

When should I use use_cache=False in a dependency?

By default, FastAPI caches dependency results for a single request. If you have a dependency that performs an action with side effects—like generating a unique timestamp or incrementing a counter—and it is called multiple times in one request, you should set Depends(my_func, use_cache=False) to ensure a fresh value is generated each time.

Can I use dependencies for Class-based logic?

Yes. FastAPI allows you to use a class as a dependency. You can define an __init__ to accept query parameters and then use the class instance in your endpoint. This is excellent for complex stateful logic or building reusable 'Service' objects.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousFastAPI Response Models and Status CodesNext →FastAPI Authentication — JWT and OAuth2 with Password Flow
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged