backend / web frameworks / fastapi / 12_di_fastapi_depends.md

What is dependency injection and how does FastAPI Depends work?

4 interview angles 1 min read source

What is dependency injection and how does FastAPI Depends work?

Answer

Dependency injection (DI) is a design pattern where a component receives its dependencies from the outside instead of creating them itself. That improves testability (you can inject mocks), flexibility (you can swap implementations), and separation of concerns.

FastAPI Depends

  • FastAPI’s Depends() declares that a route (or another dependency) needs a value that FastAPI should produce and inject.
  • FastAPI builds a dependency graph: it calls the functions you pass to Depends() and passes their return values as arguments to your route (or to other dependencies).
  • The same dependency can be reused across many routes; FastAPI caches the result per request by default (one instance per request).

Example

from fastapi import Depends

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

@app.get("/users")
def list_users(db: Session = Depends(get_db)):
    return db.query(User).all()

Here get_db is a dependency; FastAPI calls it for each request and injects the db session into list_users. So the route doesn’t create the DB session itself—it’s injected.

Interview angle

  • “What does a yield dependency give you?” - setup before the handler and guaranteed teardown afterwards, which is how a database session is opened, used and closed per request with rollback on error.
  • “Is a dependency called once per request or per use?” - cached per request by default, so two handlers depending on the same function share one instance. use_cache=False opts out when you genuinely want a fresh one.
  • “How do you apply a dependency to every route in a group?” - APIRouter(dependencies=[Depends(require_auth)]), or on the app for global ones. Repeating it in every signature is the thing this avoids.
  • “Why is dependency_overrides important?” - it’s the test seam. Swapping the database or an external client is one line, with no monkeypatching and no import-order fragility.