Вложенные зависимости и зависимости класса
Зависимости могут зависеть друг от друга, образуя граф; ими может быть и класс (через __init__), а навешивать их можно на уровне обработчика, роутера или всего приложения.
Зависимости — это конструктор: одна зависимость собирается из других. FastAPI разрешает весь граф и кэширует общие узлы, поэтому переиспользование выходит естественным.
Реальные зависимости редко плоские. «Текущий пользователь» зависит от «токена», который зависит от «заголовка». «Сервис заказов» зависит от «сессии БД». FastAPI позволяет выражать это напрямую: зависимость объявляет свои зависимости как параметры, и фреймворк разрешает всю цепочку. Общие узлы (например, сессия БД) вычисляются один раз на запрос.
from fastapi import Depends, Header, HTTPException
from typing import Annotated
def get_token(authorization: Annotated[str | None, Header()] = None):
if not authorization:
raise HTTPException(status_code=401, detail="нет токена")
return authorization.removeprefix("Bearer ")
def get_current_user(token: Annotated[str, Depends(get_token)]):
# token уже извлечён зависимостью get_token
return {"user": "ernest", "token": token}
CurrentUser = Annotated[dict, Depends(get_current_user)]
@app.get("/me")
async def me(user: CurrentUser):
return user
Класс тоже может быть зависимостью: Depends(MyService) вызовет __init__, разобрав его параметры как зависимости. А ещё зависимости можно навесить на роутер (APIRouter(dependencies=[...])) или всё приложение (FastAPI(dependencies=[...])) — тогда они применятся ко всем маршрутам, что удобно для сквозной авторизации.
Как работает под капотом
FastAPI выполняет топологическую сортировку графа: зависимость вычисляется только после своих зависимостей. Смоделируем разрешение цепочки с кэшем на stdlib:
graph = {
"header": (lambda: "Bearer xyz", []),
"token": (lambda header: header.split()[1], ["header"]),
"user": (lambda token: {"name": "ernest", "t": token}, ["token"]),
"service": (lambda user: f"OrderService для {user['name']}", ["user"]),
}
cache = {}
def resolve(name):
if name in cache:
return cache[name]
func, deps = graph[name]
cache[name] = func(*[resolve(d) for d in deps]) # сперва зависимости
return cache[name]
print(resolve("service"))
print("цепочка:", resolve("token"), "->", resolve("user")["name"])
Попробуй сам ▶ service потянул за собой user → token → header. FastAPI разрешает такие цепочки автоматически и кэширует общие узлы.
Частые ошибки
Первая — пытаться внутри зависимости вручную вызывать другую зависимость как обычную функцию вместо объявления её через Depends; так теряется кэш и разбор параметров. Вторая — забывать, что зависимости уровня приложения/роутера выполняются для всех маршрутов, включая служебные. Третья — создавать циклические зависимости. Четвёртая — складывать слишком много в один класс-зависимость, превращая его в «бога».
Best practices
- Выражайте цепочки явно: каждая зависимость объявляет свои зависимости параметрами.
- Для сквозной авторизации навешивайте зависимость на роутер или приложение, а не повторяйте в каждом обработчике.
- Класс-зависимость хорош, когда нужно сгруппировать связанные параметры/состояние.
- Избегайте циклов и чрезмерной глубины графа — это усложняет отладку.
Зависимости-фабрики с параметрами
Иногда нужна не одна зависимость, а семейство похожих: «проверь, что у пользователя есть роль X», где X разное для разных эндпоинтов. Прямо параметризовать Depends нельзя — он принимает вызываемое без аргументов. Решение — фабрика: функция, которая принимает параметр и возвращает настроенную зависимость-замыкание. Вызов require_role("admin") создаёт зависимость, проверяющую конкретную роль, и её передают в Depends. Этот приём превращает зависимости в конструктор переиспользуемых проверок: одна фабрика порождает сколько угодно специализированных стражей доступа без дублирования логики. Тот же подход работает для параметризованных лимитов, фильтров, политик кэширования. Понимание разницы между «зависимостью» и «фабрикой зависимостей» открывает целый класс элегантных решений, где поведение настраивается декларативно, прямо в сигнатуре обработчика.
Итог: зависимости образуют граф, FastAPI разрешает его в правильном порядке с кэшем общих узлов. Класс может быть зависимостью, а навешивание на роутер/приложение даёт сквозную логику для всех маршрутов.