Your Company

Зависимости в FastAPI: сессии, транзакции и тесты

Автор: Кирилл Сермягин

Зависимости — главная фишка FastAPI и главный источник утечек соединений. Если сессия базы создаётся в зависимости, важно, кто и когда её закрывает, в какой момент коммитится транзакция и как подменить всё это в тестах.

Сессия на запрос

Зависимость с yield работает как контекстный менеджер: код после yield выполнится, когда ответ уже сформирован.

from typing import Annotated

from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession


async def get_db() -> AsyncIterator[AsyncSession]:
    async with SessionLocal() as session:
        async with session.begin():
            yield session  # commit при успехе, rollback при исключении


DB = Annotated[AsyncSession, Depends(get_db)]


@app.post("/orders")
async def create_order(payload: OrderIn, db: DB) -> OrderOut:
    order = Order(**payload.model_dump())
    db.add(order)
    await db.flush()
    return OrderOut.model_validate(order)

Когда закрывается сессия

С FastAPI 0.121 у зависимости с yield можно указать scope="request" — тогда выход выполняется после отправки ответа, а по умолчанию ("function") — сразу после обработчика. Для сессии базы нужен второй вариант: соединение возвращается в пул раньше.

Подмена в тестах

В тестах подменяем зависимость одной строкой — приложение даже не узнает, что работает с тестовой базой.

@pytest.fixture
async def client(session):
    app.dependency_overrides[get_db] = lambda: session
    async with AsyncClient(transport=ASGITransport(app), base_url="http://test") as c:
        yield c
    app.dependency_overrides.clear()
  • Не создавайте движок базы внутри зависимости — только сессию.
  • Не коммитьте в обработчике, если транзакцией управляет зависимость.
  • Очищайте dependency_overrides после теста.