FastAPI Dependency Injection Basics

Dependency injection is a way to share reusable logic across multiple routes without copying and pasting code. You write the logic once as a function, and FastAPI runs it automatically before your route function executes.

The Problem It Solves

Imagine every route needs to read a database connection and check if the user is logged in:

Without dependency injection:

@app.get("/orders")
def get_orders():
    db = get_database()          ← repeated
    user = check_auth()          ← repeated
    return db.query_orders()

@app.get("/products")
def get_products():
    db = get_database()          ← repeated again
    user = check_auth()          ← repeated again
    return db.query_products()

With dependency injection, you write get_database() and check_auth() once and inject them:

@app.get("/orders")
def get_orders(db = Depends(get_database), user = Depends(check_auth)):
    return db.query_orders()

@app.get("/products")
def get_products(db = Depends(get_database), user = Depends(check_auth)):
    return db.query_products()

How Depends Works

Client sends request to /orders
         │
         ▼
FastAPI sees Depends(get_database)
  → runs get_database()
  → result stored as db
         │
FastAPI sees Depends(check_auth)
  → runs check_auth()
  → result stored as user
         │
         ▼
get_orders(db, user) runs with
already-resolved values

Writing Your First Dependency

from fastapi import FastAPI, Depends

app = FastAPI()

def get_query_settings(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

@app.get("/users")
def list_users(settings: dict = Depends(get_query_settings)):
    return {"skip": settings["skip"], "limit": settings["limit"]}

@app.get("/products")
def list_products(settings: dict = Depends(get_query_settings)):
    return {"skip": settings["skip"], "limit": settings["limit"]}
GET /users?skip=20&limit=5
  → get_query_settings(skip=20, limit=5) runs first
  → returns {"skip": 20, "limit": 5}
  → list_users receives it as settings

Both routes share the same pagination logic. Change get_query_settings once and both routes update.

Dependencies Can Have Dependencies

def get_db():
    return "database_connection"

def get_current_user(db = Depends(get_db)):
    return {"user": "Priya", "db": db}

@app.get("/me")
def my_profile(user = Depends(get_current_user)):
    return user
FastAPI resolves them in order:
  get_db()             → returns db
  get_current_user(db) → returns user
  my_profile(user)     → returns response

Class-Based Dependencies

For dependencies with multiple configurable options, use a class with __call__:

class PaginationParams:
    def __init__(self, page: int = 1, per_page: int = 20):
        self.page = page
        self.per_page = per_page
        self.skip = (page - 1) * per_page

@app.get("/articles")
def list_articles(pagination = Depends(PaginationParams)):
    return {
        "page": pagination.page,
        "per_page": pagination.per_page,
        "skip": pagination.skip
    }

Dependencies Without Return Values

Some dependencies only validate or check something without returning data. Use them as guards:

from fastapi import Header, HTTPException

def verify_api_key(x_api_key: str = Header()):
    if x_api_key != "my-secret-key":
        raise HTTPException(status_code=403, detail="Invalid API key")

@app.get("/admin", dependencies=[Depends(verify_api_key)])
def admin_panel():
    return {"panel": "admin data"}

Here dependencies=[...] on the route decorator runs the check without injecting a value into the function.

Key Points

  • Dependency injection with Depends() runs a function before your route and passes its result in.
  • Write shared logic once and reuse it across many routes.
  • Dependencies can depend on other dependencies — FastAPI resolves the whole chain automatically.
  • Use class-based dependencies for configurable, stateful logic.
  • Use dependencies=[Depends(...)] for guard-style checks that do not return values.

Leave a Comment

Your email address will not be published. Required fields are marked *