Гайды

Маршрутизация и Dependency Injection в FastAPI

APIRouter и префиксы, Depends и кэш, yield-сессии БД, dependencies на роутере, BackgroundTasks и чек-лист.

~10 мин чтения

Маршрутизация и Dependency Injection в FastAPI

APIRouter дробит приложение на модули; Depends даёт внедрение зависимостей (БД, пользователь, настройки) с кэшированием в рамках запроса. Базовый каркас — Быстрый старт с FastAPI; валидация тел — Pydantic: валидация и сериализация.


1. APIRouter и префиксы

python
# app/api/routes/users.py
from fastapi import APIRouter

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
def list_users():
    return []

@router.get("/{user_id}")
def get_user(user_id: int):
    return {"id": user_id}

Подключение:

python
from fastapi import FastAPI
from app.api.routes import users

app = FastAPI()
app.include_router(users.router, prefix="/api/v1")

Итоговый путь: /api/v1/users/, /api/v1/users/{user_id}.


2. Зависимости: функции

python
from typing import Annotated
from fastapi import Depends, Header

def pagination(skip: int = 0, limit: int = 20):
    return {"skip": skip, "limit": min(limit, 100)}

@app.get("/items")
def list_items(page: Annotated[dict, Depends(pagination)]):
    return page

Depends(pagination) вызывается при каждом запросе; результат кэшируется для одного и того же набора параметров внутри одного запроса (если зависимость повторно запрошена).


3. Зависимости: классы

python
class CommonQuery:
    def __init__(self, q: str | None = None, debug: bool = False):
        self.q = q
        self.debug = debug

@app.get("/search")
def search(cq: Annotated[CommonQuery, Depends()]):
    return {"q": cq.q, "debug": cq.debug}

Удобно группировать параметры и тестировать изолированно.


4. Вложенные Depends

python
from fastapi import Depends, Header, HTTPException

def get_token(authorization: str | None = Header(None)) -> str:
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(401, "Missing token")
    return authorization.removeprefix("Bearer ").strip()

def get_current_user(token: Annotated[str, Depends(get_token)]):
    return {"sub": "user-1"}  # здесь — разбор JWT

@app.get("/me")
def me(user: Annotated[dict, Depends(get_current_user)]):
    return user

Полноценный JWT и OAuth2 — в гайде JWT/OAuth2 и в официальном разделе Security.


5. yield в зависимостях (сессия БД)

python
from collections.abc import AsyncGenerator
from fastapi import Depends

async def get_db() -> AsyncGenerator:
    conn = await pool.acquire()
    try:
        yield conn
    finally:
        await pool.release(conn)

@app.get("/row")
async def one_row(db = Depends(get_db)):
    return await db.fetchrow("SELECT 1 AS n")

Код после yield выполняется при завершении запроса (в т.ч. после ошибки).


6. dependencies на роутере

python
router = APIRouter(dependencies=[Depends(require_api_key)])

Общие проверки для всех эндпоинтов модуля без дублирования.


7. BackgroundTasks

python
from fastapi import BackgroundTasks

def write_log(msg: str):
    ...

@app.post("/notify")
def notify(bg: BackgroundTasks, msg: str):
    bg.add_task(write_log, msg)
    return {"queued": True}

Не для тяжёлой фоновой работы — для неё отдельный воркер (Celery, RQ) и брокер задач.


8. Порядок маршрутов и mount

FastAPI сопоставляет маршруты в порядке регистрации; более специфичные пути (/items/me) объявляйте выше параметрических (/items/{item_id}), иначе me попадёт в item_id.

app.mount("/static", StaticFiles(...)) для отдачи сборки фронта; mount обычно подключают после API-роутов или на отдельном префиксе, чтобы не перехватывать JSON-эндпоинты.


9. Чек-лист

  • Роутеры по домену, единый prefix версии API.
  • Зависимости не тянут тяжёлую работу в импорт-модулей.
  • yield-ресурсы всегда освобождаются в finally.
  • Для глобального состояния приложения — app.state + lifespan.

Дальше: Быстрый старт · asyncpg · Pydantic · OpenAPI/Swagger · тег FastAPI