Mastering Dependency Injection in FastAPI: From Basics to Advanced Use
This article explains how FastAPI leverages Dependency Injection to improve code reusability, modularity, and testability, covering basic examples, sub‑dependency handling, database session management with SQLAlchemy, and building a full‑featured user management API with authentication.
Dependency Injection (DI) is a programming technique used to enhance code reusability and decouple classes from their dependencies. In the context of FastAPI, DI plays a key role in optimizing application flexibility, testability, and overall architecture.
Implementing Dependency Injection in FastAPI
FastAPI provides the Depends function to declare dependencies. Here are some common use cases.
Basic Dependency Injection
A simple example injects a function that returns a message.
<code>from fastapi import FastAPI, Depends
def get_message():
return "Hello from Dependency!"
app = FastAPI()
@app.get("/message")
def read_message(message: str = Depends(get_message)):
return {"message": message}
</code>In this example:
get_message() is a dependency.
Depends(get_message) injects its return value into the read_message() function.
Dependency Injection Methods in FastAPI
Use the Depends function.
Handle sub‑dependencies.
The Depends Function
In FastAPI, the Depends function manages dependency injection as a generic parameter for other functions, allowing orderly handling of required components across the application.
Syntax:
@app.get("URL") : decorator that registers a function to handle HTTP GET requests for the specified URL.
def main_function(value: str, list=Depends(dependency_function)) :
<code>@app.get("URL")
def main_function(value: str, list=Depends(dependency_function)):
pass
</code>The following example manages a fruit list using a dependency.
<code>from fastapi import FastAPI, HTTPException, status, Depends
# Create fruit list
main_fruits_list = ["Apple"]
app = FastAPI()
def call_main_fruits():
return main_fruits_list
@app.get("/fruits/")
def test_main_fruits(fruit: str, list=Depends(call_main_fruits)):
list.append(fruit)
print(list)
return {"message": f"Added fruit {fruit} in the list!"}
</code>Handling Sub‑Dependencies in FastAPI
Managing sub‑dependencies involves organizing additional dependent components, which promotes modular and maintainable code.
Syntax:
<code>async def dependency_function(value: int = Depends(sub_dependency_function)):
pass
</code> <code>async def sub_dependency_price(price: int):
if price == 0:
return 50
else:
return price
async def dependency_fruits(fruit: str, price: int = Depends(sub_dependency_price)):
return {"Fruit": fruit, "Price": price}
@app.get("/fruits")
async def fetch_authors(fruits_list: dict = Depends(dependency_fruits)):
return fruits_list
</code>Below is a practical example building a user‑management API with database access.
Database Dependency Injection
We use SQLAlchemy to manage database sessions and inject them where needed.
<code>pip install fastapi uvicorn sqlalchemy psycopg2 pydantic bcrypt pyjwt
</code> <code>from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base, Session
from fastapi import Depends
DATABASE_URL = "postgresql://user:password@localhost/db_name"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
</code>get_db() provides a database session as a dependency.
The yield statement ensures the session is closed after use.
Creating User Model and Authentication Service
<code>class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
</code>Authentication Service (Injectable Dependency)
We create an AuthService class that handles JWT‑based authentication.
<code>import jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
SECRET_KEY = "supersecret"
ALGORITHM = "HS256"
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class AuthService:
def hash_password(self, password: str) -> str:
return pwd_context.hash(password)
def verify_password(self, password: str, hashed_password: str) -> bool:
return pwd_context.verify(password, hashed_password)
def create_jwt_token(self, user_id: int):
expiration = datetime.utcnow() + timedelta(hours=1)
return jwt.encode({"sub": user_id, "exp": expiration}, SECRET_KEY, algorithm=ALGORITHM)
def get_auth_service():
return AuthService()
</code>AuthService is a class‑based dependency.
get_auth_service() returns an injectable instance of AuthService .
Injecting Dependencies in API Routes
<code>from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from auth import get_auth_service, AuthService
from database import get_db, User
app = FastAPI()
@app.post("/register/")
def register_user(username: str, password: str, db: Session = Depends(get_db), auth_service: AuthService = Depends(get_auth_service)):
hashed_password = auth_service.hash_password(password)
user = User(username=username, hashed_password=hashed_password)
db.add(user)
db.commit()
db.refresh(user)
return {"message": "User registered successfully"}
@app.post("/login/")
def login_user(username: str, password: str, db: Session = Depends(get_db), auth_service: AuthService = Depends(get_auth_service)):
user = db.query(User).filter(User.username == username).first()
if not user or not auth_service.verify_password(password, user.hashed_password):
raise HTTPException(status_code=400, detail="Invalid credentials")
token = auth_service.create_jwt_token(user.id)
return {"access_token": token, "token_type": "bearer"}
</code>Dependency injection in FastAPI is a powerful mechanism that enhances code modularity and maintainability. By efficiently managing and injecting dependencies, FastAPI enables developers to create well‑structured, scalable, and testable web APIs.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.