Модуль 12 · Урок 46 із 58

FastAPI dependencies, errors і async boundaries

Dependency injection робить request context і infrastructure явними, exception handlers уніфікують public failures, middleware охоплює cross-cutting behavior, а правильна async boundary не дозволяє blocking I/O зупинити event loop.

DependsErrorsMiddlewareAsync

Безпечна FastAPI-практика модуля 12

Пакет містить synthetic SprintBoard API, Pydantic v2 models, dependencies, Problem Details, middleware, TestClient і OpenAPI contract tests. Він не відкриває зовнішню мережу під час tests, не містить authentication substitutes, персональних чи production-даних, credentials або production endpoints.

Завантажити практичний пакет →

Dependency graph замінює приховані globals

Path operation оголошує, що йому потрібен repository, settings або request context, а FastAPI розв’язує dependency graph для кожного request. Dependency може мати власні sub-dependencies та впливати на OpenAPI parameters.

from typing import Annotated
from fastapi import Depends

def get_repository() -> TaskRepository:
    return repository

RepositoryDep = Annotated[TaskRepository, Depends(get_repository)]

@app.get("/tasks/{task_id}")
async def get_task(task_id: int, repo: RepositoryDep):
    return repo.get(task_id)

У tests app.dependency_overrides[get_repository] підміняє production provider isolated fake. Override очищають після test, інакше state протече до наступного scenario.

Resource lifecycle оформлюють через yield

Dependency з yield створює resource, передає його handler і виконує cleanup. Це підходить для database session або scoped client, але transaction policy та exception behavior треба задати явно. Cleanup має працювати й при failure.

def get_session():
    session = SessionFactory()
    try:
        yield session
    finally:
        session.close()

Connection pool є application-scoped infrastructure, а session — request-scoped unit. Створення нового pool на кожен request є дорогою помилкою.

Domain failure мапиться на HTTP один раз

Repository або service може підняти TaskNotFound чи TitleConflict. Web layer перетворює їх на 404/409 і safe structured representation. HTTPException зручний на boundary, але domain core краще не прив’язувати до FastAPI.

Safe Problem Details

Public type, title, status, detail та request-scoped instance не включають SQL, stack trace, credentials, internal paths або raw body.

Request validation failures обробляє RequestValidationError. Handler може нормалізувати shape, але не повинен перетворювати application bug у начебто client error.

Middleware обгортає кожен request

Middleware виконує код до route і після response. Тут доречні request ID, coarse timing, security headers або tracing context. Authorization часто краще виражати dependencies на конкретних routes; один global middleware не знає всіх domain rules.

Кілька middleware утворюють stack: останнє додане є outermost, на request виконується першим, на response — останнім. Order перевіряють test, особливо для CORS, errors і tracing.

def та async def обирають за I/O contract

Якщо library надає awaitable I/O, path operation роблять async def і викликають її через await. Blocking library можна викликати з normal def handler/dependency, який FastAPI запускає у threadpool. Виклик blocking network, filesystem або database code прямо всередині async def блокує event loop.

OperationBoundaryRisk
Async database driverasync def + awaitЗабутий await
Blocking SDKdef або explicit thread offloadEvent-loop stall
CPU-heavy transformQueue/process boundaryThreadpool не дає CPU parallelism

Timeout, cancellation і cleanup є частиною design; просто додати async до function name недостатньо.

Методичні джерела

Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть на офіційну документацію framework та стандартів.

Практична перевірка · урок 46 з 47

Закріпіть матеріал уроку

Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.

1. Handler читає module-level database session, яку ділять усі requests. Яка FastAPI design зміна робить lifecycle явним?
2. Що потрібно передати в Depends для function dependency get_repository?
3. Dependency відкриває database session. Який pattern гарантує cleanup і після success, і після exception?