Skip to main content

FastAPI Dependency Injection: Best Use Cases for Beginners

ยท 6 min read
Serhii Hrekov
software engineer, creator, artist, programmer, projects founder

FastAPI's Dependency Injection (DI) system, powered by the Depends function, is a powerful concept built on simple Python functions. For beginners, the best use cases are those that demonstrate code reuse, logic separation, and early request validation without requiring complex external libraries.

By focusing on these three patterns, you learn to keep your route functions clean and focus only on the core business logic.

1. Use Case: Injecting Application Settings (The Simplest Start)โ€‹

The easiest way to understand DI is to use it to inject application settings or configuration objects, ensuring these values are available everywhere without using global variables.

Example Codeโ€‹

from fastapi import FastAPI, Depends
from typing import Annotated
import os

app = FastAPI()

# 1. Dependency: Retrieves application settings
def get_settings():
"""Reads settings (in a real app, this would read from environment variables)."""
return {
"api_name": "ProductAPI",
"debug_mode": True,
"default_limit": 10
}

# Type hint the dependency for clean route signatures
Settings = Annotated[dict, Depends(get_settings)]

@app.get("/info")
def read_info(settings: Settings):
"""The 'settings' dictionary is injected here."""
return {
"name": settings["api_name"],
"mode": settings["debug_mode"]
}

# Annotation: The `get_settings` function runs once per request, and its
# dictionary return value is passed directly to the route function's `settings` argument.

2. Use Case: Validating and Defaulting Query Parametersโ€‹

DI is excellent for consolidating complex input validation logic that might otherwise clutter your route function. You can define a dependency that takes request parameters, validates them, and provides a cleaned result.

Example Codeโ€‹

from fastapi import Depends, Query, HTTPException

# 2. Dependency: Validates and converts the requested limit/offset
def pagination_params(
limit: Annotated[int, Query(ge=1, le=100)] = 10,
offset: Annotated[int, Query(ge=0)] = 0
):
"""
Enforces minimum and maximum values for pagination inputs.
"""
# Custom validation logic
if limit + offset > 500:
raise HTTPException(
status_code=400,
detail="Cannot query more than 500 items in total."
)

return {"limit": limit, "offset": offset}

# Define the type hint for the dependency
Pagination = Annotated[dict, Depends(pagination_params)]

@app.get("/items")
def get_items(page: Pagination):
"""
The route receives a clean dictionary of validated pagination parameters.
"""
# Core business logic uses the pre-validated values
return {
"message": f"Fetching items with limit={page['limit']} and offset={page['offset']}"
}

# Annotation: The `pagination_params` dependency takes the Query parameters
# directly. If validation fails, the HTTPException stops the request before the
# `get_items` function is ever called.

3. Use Case: Basic Security Check (Required Header)โ€‹

This is a perfect beginner case to centralize simple authentication logic. The dependency runs first and can immediately stop the request by raising an exception if the required header or token is missing or invalid.

Example Codeโ€‹

from fastapi import Depends, Header, HTTPException

# 3. Dependency: Checks for a required header
async def verify_api_key(api_key: Annotated[str | None, Header(alias="X-Internal-Key")] = None):
"""
Checks for a required header and raises 401 if it's missing or wrong.
"""
EXPECTED_KEY = "my-secure-token-123"

if api_key != EXPECTED_KEY:
raise HTTPException(
status_code=401,
detail="Authorization required: Invalid X-Internal-Key."
)
# If successful, the dependency implicitly returns None (or True, if desired)

# Define the type hint for required access
InternalAccess = Annotated[None, Depends(verify_api_key)]

@app.post("/admin/report")
def create_report(access: InternalAccess):
"""
This route will only execute if verify_api_key completes without raising an error.
"""
return {"status": "Report created by authorized user."}

# Annotation: The dependency function can be synchronous or asynchronous.
# Using the type hint `Annotated[None, ...]` clearly signals that the dependency
# is used for validation/side-effects, and its return value is ignored.

Sources and Further Readingโ€‹

  1. FastAPI Documentation - Dependencies Introduction
  2. FastAPI Documentation - Dependencies in Path Operation Decorators
  3. FastAPI Documentation - Security and Dependencies