FastAPI Dependency Injection: Your Magic Helper System 🪄
The Big Idea in 30 Seconds
Imagine you’re building a LEGO castle. Instead of searching for the right pieces every time, what if a helper automatically brought you exactly what you need?
That’s Dependency Injection! Your code asks for help, and FastAPI delivers it automatically.
🎠Our Story: The Restaurant Kitchen
Picture a busy restaurant kitchen. The head chef (your endpoint) needs ingredients, tools, and helpers to cook meals.
Without Dependency Injection:
Chef runs around grabbing salt, finding knives, checking if the fridge is working… exhausting!
With Dependency Injection:
Chef just says “I need salt” and poof — it appears. Magic helpers handle everything!
1. Dependency Injection Basics
What Is It?
Dependency Injection means: “Don’t create things yourself. Ask for them, and they’ll be given to you.”
Think of it like ordering food. You don’t go to the kitchen. You tell the waiter what you want, and they bring it to you.
Why Does This Matter?
| Without DI | With DI |
|---|---|
| Your code creates everything | Helpers provide what you need |
| Hard to test | Easy to test |
| Messy spaghetti code | Clean, organized code |
Simple Mental Model
graph TD A[Your Endpoint] -->|Asks for| B[Dependency] C[FastAPI] -->|Provides| B B -->|Ready to use!| A
Your endpoint just asks. FastAPI delivers.
2. The Depends Function
Your Magic Word
Depends() is like saying “please” to FastAPI. It tells FastAPI: “I need this thing. Please get it for me.”
Your First Dependency
from fastapi import Depends, FastAPI
app = FastAPI()
# This is the helper function
def get_greeting():
return "Hello!"
# Use Depends to ask for it
@app.get("/")
def read_root(
greeting: str = Depends(get_greeting)
):
return {"message": greeting}
What happens:
- Someone visits
/ - FastAPI sees
Depends(get_greeting) - FastAPI calls
get_greeting()for you - Your function receives
"Hello!"
Real-World Example: Getting User Info
def get_current_user():
# Check login, read token, etc.
return {"name": "Alice", "role": "admin"}
@app.get("/profile")
def profile(
user: dict = Depends(get_current_user)
):
return f"Welcome, {user['name']}!"
You never call get_current_user() yourself. FastAPI does it!
Dependencies with Parameters
def get_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
@app.get("/items")
def read_items(
pagination: dict = Depends(get_items)
):
return pagination
Visit /items?skip=5&limit=20 and FastAPI:
- Reads
skipandlimitfrom the URL - Passes them to
get_items - Gives you the result
3. Class-based Dependencies
When Functions Aren’t Enough
Sometimes you need more than a simple function. You need an object with state or configuration.
The Class as a Dependency
class Paginator:
def __init__(
self,
skip: int = 0,
limit: int = 10
):
self.skip = skip
self.limit = limit
@app.get("/books")
def get_books(
page: Paginator = Depends(Paginator)
):
return {
"skip": page.skip,
"limit": page.limit
}
How It Works
graph TD A[Request: /books?skip=5] --> B[FastAPI] B -->|Creates| C[Paginator object] C -->|skip=5, limit=10| D[Your Endpoint]
FastAPI creates the Paginator object and fills in the values from the request!
Why Use Classes?
- Reusable — Use the same paginator everywhere
- Type hints — Your IDE knows what
page.skipis - Validation — Add checks inside
__init__
class Paginator:
def __init__(
self,
skip: int = 0,
limit: int = 100
):
if limit > 100:
limit = 100 # Safety limit!
self.skip = skip
self.limit = limit
4. Sub-dependencies
Dependencies That Need Other Dependencies
What if your helper needs helpers? Like a chef who needs a sous-chef, who needs a prep cook?
Chain of Dependencies
def get_database():
return {"connected": True}
def get_user_service(
db: dict = Depends(get_database)
):
return {"db": db, "service": "users"}
@app.get("/users")
def list_users(
service: dict = Depends(get_user_service)
):
return service
What Happens
graph TD A["/users endpoint"] -->|needs| B[get_user_service] B -->|needs| C[get_database] C -->|returns| D[database connection] D -->|used by| B B -->|returns| E[user service] E -->|used by| A
FastAPI automatically resolves the whole chain!
Real-World Example: Auth Chain
def get_token(
authorization: str = Header(...)
):
return authorization.split(" ")[1]
def get_current_user(
token: str = Depends(get_token)
):
# Decode token, find user
return {"id": 1, "name": "Alice"}
def get_admin_user(
user: dict = Depends(get_current_user)
):
if user.get("role") != "admin":
raise HTTPException(403)
return user
@app.get("/admin/dashboard")
def admin_panel(
admin: dict = Depends(get_admin_user)
):
return {"welcome": admin["name"]}
Three levels deep — FastAPI handles it all!
5. Dependencies with Yield
Setup AND Cleanup
Sometimes you need to:
- Set up something before your endpoint runs
- Clean up after your endpoint finishes
Like opening a door, doing work, then closing the door.
The Yield Pattern
def get_db_connection():
# SETUP: Open connection
db = DatabaseConnection()
db.connect()
yield db # Give to endpoint
# CLEANUP: Close connection
db.close()
How It Works
graph TD A[Request arrives] --> B[SETUP: Open DB] B --> C[yield db] C --> D[Endpoint runs] D --> E[Endpoint returns] E --> F[CLEANUP: Close DB] F --> G[Response sent]
Everything after yield runs AFTER your endpoint finishes!
Complete Example
def get_file_handler():
# Setup
file = open("data.txt", "r")
print("File opened!")
yield file
# Cleanup (always runs!)
file.close()
print("File closed!")
@app.get("/read")
def read_file(
f = Depends(get_file_handler)
):
return {"content": f.read()}
Why Yield Matters
| Without Yield | With Yield |
|---|---|
| You forget to close connections | Auto-cleanup guaranteed |
| Memory leaks | Resources always freed |
| Manual try/finally everywhere | Clean, simple code |
6. Global Dependencies
Apply to EVERYTHING
What if you want the same dependency on every single endpoint?
Like a security guard checking ID at the building entrance — not at every room.
Adding Global Dependencies
from fastapi import Depends, FastAPI
def verify_api_key(
api_key: str = Header(...)
):
if api_key != "secret123":
raise HTTPException(401)
return api_key
# Apply to ALL routes!
app = FastAPI(
dependencies=[Depends(verify_api_key)]
)
@app.get("/")
def home():
return {"msg": "You're authorized!"}
@app.get("/data")
def data():
return {"secret": "stuff"}
Both endpoints automatically check the API key!
Router-Level Dependencies
You can also apply to a group of routes:
from fastapi import APIRouter
admin_router = APIRouter(
prefix="/admin",
dependencies=[Depends(get_admin_user)]
)
@admin_router.get("/stats")
def stats():
return {"users": 100}
@admin_router.get("/logs")
def logs():
return {"entries": []}
app.include_router(admin_router)
All /admin/* routes require admin access!
Levels of Dependencies
graph TD A[Global Dependencies] -->|apply to| B[Entire App] C[Router Dependencies] -->|apply to| D[Route Group] E[Endpoint Dependencies] -->|apply to| F[Single Route]
Quick Summary
| Concept | What It Does | When to Use |
|---|---|---|
| Depends() | Asks for a helper | Every dependency |
| Class Dependencies | Creates objects | Shared configuration |
| Sub-dependencies | Chain of helpers | Complex setups |
| Yield Dependencies | Setup + cleanup | Database, files, locks |
| Global Dependencies | Applies everywhere | Auth, logging, headers |
Your Journey So Far 🎉
You’ve learned that Dependency Injection is like having magic helpers:
- You ask (using
Depends) - FastAPI provides (creating, chaining, cleaning up)
- You focus on your code (not plumbing)
No more hunting for ingredients. No more forgetting to close doors. Just clean, powerful, professional code.
You’re ready to build amazing things! 🚀