FastAPI Dependency Injection for Database Sessions

FastAPI utilizes a robust dependency injection system powered by the Depends class and Python generator functions to manage database sessions seamlessly across endpoints. By defining a dependency that yields a database session, FastAPI ensures that each incoming HTTP request receives an isolated session that automatically handles commit, rollback, and cleanup logic when the request lifecycle concludes.

The Session Generator Function

The foundation of sharing a database session lies in a generator function, commonly named get_db. Using an ORM like SQLAlchemy, this function creates a session instance, yields it to the route handler, and ensures the session is closed afterward using a try...finally block.

from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

DATABASE_URL = "sqlite:///./test.db"

engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()


def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

The yield statement is critical. Code execution pauses at yield db, passing the active session object to whichever route requested it. Once the route finishes processing and sends an HTTP response, execution resumes immediately after the yield, executing the finally block to close the database connection.

Injecting Sessions into Routes

To consume the database session within an API endpoint, declare a parameter in the route function and assign it to Depends(get_db). FastAPI automatically evaluates the dependency, invokes the generator, and injects the resulting session object into the route parameter.

from fastapi import Depends, FastAPI
from sqlalchemy.orm import Session

app = FastAPI()


@app.get("/items/")
def read_items(db: Session = Depends(get_db)):
    items = db.query(ItemModel).all()
    return items

Request Lifecycle and Concurrency

FastAPI ensures thread-safety and proper scoping for each connection:

Overriding for Testing

A major advantage of this architecture is testability. FastAPI allows dependency overrides without altering production code. During automated tests, you can swap the production get_db dependency with a test-specific session connected to a mock or an in-memory database:

app.dependency_overrides[get_db] = get_test_db

This modular approach keeps route handlers lightweight, enforces separation of concerns, and simplifies connection lifecycle management across the application.