How to Implement Dependency Injection in FastAPI
# Understanding Dependency Injection in FastAPI
Several languages and frameworks rely heavily on **dependency injection** — no pun intended. Go, Angular, NestJS, and Python's **FastAPI** all use it as a core architectural pattern.
If you've worked with FastAPI, you've likely encountered dependencies in action. Maybe you’ve seen `Depends()` in a tutorial or the official documentation and felt confused — I certainly did at first. That confusion led to weeks of experimenting with FastAPI's dependency system.
The truth is: **dependency injection in FastAPI is unavoidable** when building backend services. It’s embedded into the framework’s core, powering functionalities such as:
- Authentication
- Database connections
- Request validation
FastAPI’s documentation describes its DI system as *“powerful but intuitive”*. Once you understand the fundamentals, that’s spot on.
This article breaks it down into:
- Function-based dependencies
- Class-based dependencies
- Dependency scopes
- Practical usage examples
---
## Table of Contents
- [Prerequisites](#prerequisites)
- [Dependencies and Dependency Injection in FastAPI](#dependencies-and-dependency-injection-in-fastapi)
- [Getting Started: Environment Setup](#getting-started-environment-setup)
- [Types of Dependencies in FastAPI](#types-of-dependencies-in-fastapi)
- [Function Dependencies](#function-dependencies)
- [Class Dependencies](#class-dependencies)
- [Dependency Scope](#dependency-scope)
- [Path Operation Level](#path-operation-level)
- [Router Level](#router-level)
- [Application Level](#application-level)
- [Common Use Cases](#common-use-cases)
- [Conclusion](#conclusion)
---
## Prerequisites
You should have:
- Working knowledge of **Python**
- Ability to create and activate **virtual environments**
- Basic understanding of **FastAPI**
- Familiarity with **OOP** concepts
---
## Dependencies and Dependency Injection in FastAPI
### What’s a Dependency?
A **dependency** is reusable logic that your path operations can require — for example, authentication, DB connection, or validation.
### What’s Dependency Injection?
**Dependency injection (DI)** is the mechanism FastAPI uses to provide these dependencies to the parts of the app where they’re needed.
You declare them using `Depends()`, and FastAPI automatically executes them before running the endpoint.
**Benefits**:
- Avoids repetitive setup code in each route
- Keeps applications **modular** and **scalable**
- Reduces bugs and maintenance overhead
---
## Getting Started: Environment Setup
1. **Create & activate a virtual environment**:
python -m venv deps
source deps/bin/activate # macOS / Linux
deps\Scripts\activate # Windows
2. **Install FastAPI**:
pip install 'fastapi[all]'
3. **Organize your project structure**:
project/
├── main.py
├── deps/
│ └── __init__.py
---
## Types of Dependencies in FastAPI
A dependency can be any **callable object** that performs checks or retrieves data before an endpoint runs.
- **Function Dependencies** — Simple, ideal for most cases: validation, auth, data fetch
- **Class Dependencies** — Useful when you need state, OOP patterns, or multiple configurations
---
## Function Dependencies
The easiest method is defining a helper function.
Example: In `fastapi-deps/function_deps.py`:
### Step 1: Imports
from fastapi import FastAPI, Depends, HTTPException
import uvicorn
### Step 2: Create Application
app = FastAPI()
### Step 3: In-Memory Database
users = [
{"name": "Ore", "password": "jkzvdgwya12"},
{"name": "Uche", "password": "lga546"},
{"name": "Seke", "password": "SK99!"},
# ...
]
**Note:** This storage is non-persistent. Data is lost on restart.
### Step 4: Dependency Function
def user_dep(name: str, password: str):
for u in users:
if u["name"] == name and u["password"] == password:
return {"name": name, "valid": True}
### Step 5: Inject Dependency into Route
@app.get("/users/{user}")
def get_user(user = Depends(user_dep)) -> dict:
if not user:
raise HTTPException(status_code=401, detail="Invalid username or password")
return user
---
### Start & Test the Server
uvicorn function_deps:app --reload
Test in browser/Postman:
http://127.0.0.1:8000/users/{user}?name=Seke&password=SK99!
Or via Swagger docs:
http://127.0.0.1:8000/docs
---
## Class Dependencies
Ideal for **stateful** logic or complex initializations.
### Authentication Class Example
class UserAuth:
def __init__(self, name: str, password: str):
self.name = name
self.password = password
def __call__(self):
for user in users:
if user["name"] == self.name and user["password"] == self.password:
return
raise HTTPException(status_code=401, detail="Invalid username or password")
### Use in Route
@app.get("/user/dashboard")
def get_dashboard(user: UserAuth = Depends(UserAuth)):
return {"message": f"Access granted to {user.name}"}
---
## Dependency Scope
FastAPI allows injecting dependencies on different levels:
1. **Path Operation Level**
2. **Router Level**
3. **Application Level**
---
### Path Operation Level
def user_dep(name: str, password: str):
for u in users:
if u["name"] == name and u["password"] == password:
return
raise HTTPException(status_code=401, detail="Invalid username or password")
@app.get("/users/{user}", dependencies=[Depends(user_dep)])
def get_user() -> dict:
return {"message": "Access granted!"}
The dependency runs **before** the endpoint but **does not** pass its return value.
---
### Router Level
`fastapi-deps/router_deps.py`:
from fastapi import APIRouter, Depends
from function_deps import user_dep
router = APIRouter(prefix="/users", dependencies=[Depends(user_dep)])
@router.get("/{user}")
def get_user() -> dict:
return {"message": "Access granted!"}
`app.py`:
from fastapi import FastAPI
from router_deps import router as user_router
import uvicorn
app = FastAPI()
app.include_router(user_router)
if __name__ == "__main__":
uvicorn.run("app:app", reload=True)
---
### Application Level
Global dependencies run on **every** route.
from fastapi import FastAPI, Depends
from function_deps import user_dep
from router_deps import router as user_router
from datetime import datetime
import uvicorn
def log_request():
print(f"[{datetime.now()}] Request received.")
app = FastAPI(dependencies=[Depends(log_request), Depends(user_dep)])
app.include_router(user_router)
@app.get("/home")
def get_main():
return "Welcome back!!!"
if __name__ == "__main__":
uvicorn.run("app:app", reload=True)
---
## Common Use Cases
- **Database connections** — Avoid connection leaks
- **Auth & authorization** — Token validation, role checking
- **Logging & monitoring**
- **Rate limiting**
- **Config injection** — Environment variables, API keys
- **Pagination/filtering** — Shared query parameter logic
---
## Conclusion
FastAPI’s DI system enables **clean**, **reusable**, and **maintainable** code.
While not necessary for simple one-off logic, DI shines when managing shared logic, lifecycles, and modular designs.
It reduces repetition, centralizes logic, and improves scalability — making it a key part of any serious FastAPI project.