Advanced Python Dependency Injection with Pydantic and FastAPI

I am a Tech Enthusiast having 13+ years of experience in ๐๐ as a ๐๐จ๐ง๐ฌ๐ฎ๐ฅ๐ญ๐๐ง๐ญ, ๐๐จ๐ซ๐ฉ๐จ๐ซ๐๐ญ๐ ๐๐ซ๐๐ข๐ง๐๐ซ, ๐๐๐ง๐ญ๐จ๐ซ, with 12+ years in training and mentoring in ๐๐จ๐๐ญ๐ฐ๐๐ซ๐ ๐๐ง๐ ๐ข๐ง๐๐๐ซ๐ข๐ง๐ , ๐๐๐ญ๐ ๐๐ง๐ ๐ข๐ง๐๐๐ซ๐ข๐ง๐ , ๐๐๐ฌ๐ญ ๐๐ฎ๐ญ๐จ๐ฆ๐๐ญ๐ข๐จ๐ง ๐๐ง๐ ๐๐๐ญ๐ ๐๐๐ข๐๐ง๐๐. I have ๐๐๐๐๐๐๐ ๐๐๐๐ ๐๐๐๐ 10,000+ ๐ฐ๐ป ๐ท๐๐๐๐๐๐๐๐๐๐๐๐ and ๐๐๐๐ ๐๐๐๐๐ ๐๐๐๐ ๐๐๐๐ 500+ ๐๐๐๐๐๐๐๐ ๐๐๐๐๐๐๐๐ in the areas of ๐๐จ๐๐ญ๐ฐ๐๐ซ๐ ๐๐๐ฏ๐๐ฅ๐จ๐ฉ๐ฆ๐๐ง๐ญ, ๐๐๐ญ๐ ๐๐ง๐ ๐ข๐ง๐๐๐ซ๐ข๐ง๐ , ๐๐ฅ๐จ๐ฎ๐, ๐๐๐ญ๐ ๐๐ง๐๐ฅ๐ฒ๐ฌ๐ข๐ฌ, ๐๐๐ญ๐ ๐๐ข๐ฌ๐ฎ๐๐ฅ๐ข๐ณ๐๐ญ๐ข๐จ๐ง๐ฌ, ๐๐ซ๐ญ๐ข๐๐ข๐๐ข๐๐ฅ ๐๐ง๐ญ๐๐ฅ๐ฅ๐ข๐ ๐๐ง๐๐ ๐๐ง๐ ๐๐๐๐ก๐ข๐ง๐ ๐๐๐๐ซ๐ง๐ข๐ง๐ . I am interested in ๐ฐ๐ซ๐ข๐ญ๐ข๐ง๐ ๐๐ฅ๐จ๐ ๐ฌ, ๐ฌ๐ก๐๐ซ๐ข๐ง๐ ๐ญ๐๐๐ก๐ง๐ข๐๐๐ฅ ๐ค๐ง๐จ๐ฐ๐ฅ๐๐๐ ๐, ๐ฌ๐จ๐ฅ๐ฏ๐ข๐ง๐ ๐ญ๐๐๐ก๐ง๐ข๐๐๐ฅ ๐ข๐ฌ๐ฌ๐ฎ๐๐ฌ, ๐ซ๐๐๐๐ข๐ง๐ ๐๐ง๐ ๐ฅ๐๐๐ซ๐ง๐ข๐ง๐ new subjects.
Introduction
Modern backend architectures demand modular, maintainable, and testable code. One of the cornerstones of achieving this is Dependency Injection (DI) โ a software design pattern that helps decouple object creation from business logic, making applications easier to scale, test, and extend.
While traditional frameworks like Django use settings modules and class-based views for configuration and service management, FastAPI takes this a step further with its native, lightweight, and elegant dependency injection system built on function parameters and Pythonโs async capabilities.
When combined with Pydantic, FastAPIโs preferred data validation and parsing library, it enables powerful, type-safe, and scalable dependency management โ ideal for modern, asynchronous web services.
This guide will take you through:
What Dependency Injection is
How FastAPI implements DI via
DependsUsing Pydantic models as dependencies
Injecting services, config settings, database sessions
Advanced DI techniques like sub-dependencies and class-based dependencies
Best practices for clean, testable architectures
Letโs get into it boss.
What Is Dependency Injection?
Dependency Injection (DI) is a design pattern where the required dependencies of a component (like a function, class, or service) are provided externally rather than being created inside the component itself.
Benefits:
Decouples modules and services
Simplifies unit testing via mock dependencies
Enhances code reusability and readability
Allows flexible configuration swapping
Dependency Injection in FastAPI
FastAPI uses a lightweight, intuitive DI system based on its Depends class.
Basic example:
from fastapi import FastAPI, Depends
app = FastAPI()
def get_token():
return "secure-token-123"
@app.get("/items/")
def read_items(token: str = Depends(get_token)):
return {"token": token}
How it works:
Depends(get_token)tells FastAPI to executeget_token()and inject the return value as thetokenparameter.Dependencies can be synchronous or asynchronous.
Multiple dependencies can be stacked per route.
Pydantic-Powered Dependency Models
Pydantic is FastAPIโs native way to define structured, validated data. You can use Pydantic models as dependencies to enforce input schemas for injected configurations or objects.
Example:
from pydantic import BaseModel
class Config(BaseModel):
api_key: str
timeout: int
def get_config():
return Config(api_key="xyz-123", timeout=10)
@app.get("/settings/")
def get_settings(config: Config = Depends(get_config)):
return config
Benefits:
Enforces type safety on injected objects
Auto-validates dependencies
Provides clear API contracts for developers
Dependency Injection for Services
In scalable apps, you often need to inject services like database clients, authentication modules, or external API clients.
Example:
class Database:
def __init__(self, url):
self.url = url
def connect(self):
return f"Connected to {self.url}"
def get_db():
return Database(url="sqlite:///mydb.db")
@app.get("/db/")
def connect_to_db(db: Database = Depends(get_db)):
return {"message": db.connect()}
Now, Database instances are provided to each request dynamically.
Dependency Overriding (Test-Friendly Design)
FastAPI allows dependency overriding at runtime โ making it perfect for injecting test mocks.
Example:
def override_config():
return Config(api_key="mock-api", timeout=1)
app.dependency_overrides[get_config] = override_config
Now, all routes depending on get_config() will use the override โ essential for isolated testing.
Asynchronous Dependencies
FastAPI seamlessly supports async def dependencies.
Example:
import asyncio
async def get_data():
await asyncio.sleep(1)
return "async data"
@app.get("/data/")
async def read_data(data: str = Depends(get_data)):
return {"data": data}
Sub-Dependencies (Dependencies of Dependencies)
One dependency can depend on another, creating a dependency graph.
Example:
def get_username():
return "vinaykumar"
def get_message(username: str = Depends(get_username)):
return f"Hello {username}"
@app.get("/welcome/")
def welcome(message: str = Depends(get_message)):
return {"message": message}
FastAPI handles sub-dependencies automatically, resolving them in order.
Class-Based Dependencies
For complex services with state or methods, FastAPI supports classes as dependencies.
Example:
class Auth:
def __init__(self, api_key: str):
self.api_key = api_key
def verify(self):
return self.api_key == "secure-key"
def get_auth():
return Auth(api_key="secure-key")
@app.get("/secure/")
def secure(auth: Auth = Depends(get_auth)):
if not auth.verify():
return {"error": "Unauthorized"}
return {"message": "Welcome Vinay"}
Benefits:
Encapsulates logic cleanly
Easy to override or mock
Supports per-request state
Global Configuration Injection
Large apps often require global config like database URLs, secrets, or environment-specific values. FastAPI encourages using Pydanticโs BaseSettings.
Example:
from pydantic import BaseSettings
class Settings(BaseSettings):
database_url: str = "sqlite:///prod.db"
secret_key: str = "secret-123"
settings = Settings()
def get_settings():
return settings
@app.get("/config/")
def config_endpoint(conf: Settings = Depends(get_settings)):
return conf.dict()
Advantages:
Supports
.envfiles and environment variablesCentralized config management
Type-safe and validated at startup
Scoped vs Shared Dependencies
By default:
Functions and classes are per-request
You can force a singleton dependency (one instance reused across requests) by caching it in memory (not advised for mutable/shared state unless carefully managed)
Dependency Caching (Using Depends(cache=True))
FastAPI allows caching dependencies for a single request lifecycle.
Example:
from fastapi import Request
def get_unique_id(request: Request):
return id(request)
@app.get("/id/")
def id_view(uid: int = Depends(get_unique_id)):
return {"id": uid}
To cache:
@app.get("/cached/")
def cached_view(uid: int = Depends(get_unique_id, use_cache=True)):
return {"cached_id": uid}
This prevents redundant calls to the same dependency within a single request.
Dependency Injection for Background Tasks
BackgroundTask instances can also be injected via DI.
Example:
from fastapi import BackgroundTasks
def write_log(message: str):
with open("log.txt", "a") as f:
f.write(f"{message}\n")
@app.post("/notify/")
def notify_user(background_tasks: BackgroundTasks):
background_tasks.add_task(write_log, "User Notified")
return {"status": "Task Scheduled"}
Clean and non-blocking.
Dependency Injection and Middleware
Dependencies can interact with middleware too โ enabling powerful observability or authentication hooks.
Example:
Dependency returns user from token
Middleware validates or logs users based on DI resolution
This creates an elegant separation of concerns for cross-cutting concerns.
Conclusion
FastAPIโs native Dependency Injection system is one of the most elegant and powerful features in the modern Python backend ecosystem.
Combined with Pydantic, it creates:
Highly modular APIs
Clean, testable services
Decoupled configuration management
Scalable microservices architecture
Whether youโre injecting a simple API key, a full-fledged ORM session, or a chain of dependent services, FastAPIโs Depends decorator makes it seamless.
By adopting these advanced DI patterns:
You reduce coupling in your codebase
Simplify unit and integration testing
Build APIs that are clean, flexible, and future-proof
Mastering Dependency Injection with FastAPI and Pydantic will unlock scalable, enterprise-grade application design in your Python projects.


