Как работает dependency injection в FastAPI
Что проверяют
Понимаете ли вы, что Depends это не контейнер с временем жизни объектов, а граф функций, который FastAPI разрешает на каждый запрос. Следом почти всегда спрашивают, как подменить зависимость в тестах.
Как отвечать
Опишите механику по шагам:
- объявление: зависимость это любой вызываемый объект, функция или класс. Современный стиль через
Annotated:db: Annotated[Session, Depends(get_db)], такой псевдоним удобно переиспользовать; - граф: у зависимости могут быть свои зависимости. FastAPI разбирает сигнатуры, строит дерево и берёт параметры из пути, query, заголовков и тела, всё это попадает в OpenAPI-схему;
- кэш на запрос: если зависимость встречается в дереве несколько раз, в пределах запроса она вычисляется один раз. Отключается через
Depends(get_x, use_cache=False); - зависимости с
yield: код доyieldготовит ресурс, код после освобождает, например закрывает сессию базы; - уровни:
dependencies=[Depends(verify_token)]в декораторе, уAPIRouterили у всегоFastAPI(), когда нужен не результат, а проверка; - sync и async:
async defвыполняется в event loop, обычнаяdefуходит в пул потоков.
Для тестов есть app.dependency_overrides[get_db] = override_get_db: подменяете сессию на тестовую, и эндпоинт об этом не знает. После теста словарь очищают.
Ресурсы на всё приложение, пул соединений или HTTP-клиент, создают в lifespan, а зависимость лишь отдаёт их из app.state.
Где спотыкаются
Ждут от Depends синглтонов. Функция-зависимость вызывается заново на каждый запрос, и тяжёлый объект внутри неё будет создаваться постоянно.
Пишут блокирующий код в async def зависимости: синхронный драйвер базы внутри асинхронной функции останавливает весь event loop. Если драйвер синхронный, зависимость должна быть обычной def.
Используют ресурс из yield-зависимости в BackgroundTasks. Момент выполнения кода после yield относительно отправки ответа менялся между версиями FastAPI, и фоновая задача может получить уже закрытую сессию. Фоновой задаче надёжнее открывать свой ресурс.