Маршрутизация, теги и APIRouter
APIRouter — это переносной «мини-роутер», позволяющий разбить приложение на модули и собрать их в одно приложение через include_router с общим префиксом и тегами.
По мере роста проекта весь код не должен жить в одном файле.
APIRouterгруппирует связанные маршруты (например, всё про пользователей) и подключается к приложению одной строкой.
Когда эндпоинтов становится десятки, держать их в одном main.py невыносимо. FastAPI предлагает разрезать API на логические модули — по ресурсам или доменам. Каждый модуль создаёт свой APIRouter, регистрирует на нём маршруты теми же декораторами, а главный app подключает роутеры. Это даёт изоляцию, переиспользование и аккуратную документацию, сгруппированную по тегам.
# users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
async def list_users():
return [{"id": 1}, {"id": 2}]
@router.get("/{user_id}")
async def get_user(user_id: int):
return {"id": user_id}
# main.py
from fastapi import FastAPI
from users import router as users_router
app = FastAPI()
app.include_router(users_router)
Префикс /users добавляется ко всем маршрутам роутера, поэтому @router.get("/") станет GET /users/. Тег users сгруппирует эти эндпоинты в одну секцию документации. Можно также навешивать на весь роутер общие зависимости (об этом — в разделе про зависимости).
Как работает под капотом
include_router не делает ничего волшебного: он берёт маршруты роутера, приклеивает к каждому пути префикс, добавляет общие теги и зависимости и регистрирует получившиеся маршруты в таблице приложения. По сути это слияние двух таблиц маршрутов с трансформацией путей. Смоделируем на stdlib:
class Router:
def __init__(self, prefix="", tags=None):
self.prefix = prefix
self.tags = tags or []
self.routes = []
def get(self, path):
def deco(fn):
self.routes.append(("GET", path, fn))
return fn
return deco
app_routes = []
def include_router(router):
for method, path, fn in router.routes:
full = router.prefix + path # приклеиваем префикс
app_routes.append((method, full, router.tags, fn))
users = Router(prefix="/users", tags=["users"])
@users.get("/")
def list_users():
return ["u1", "u2"]
@users.get("/{user_id}")
def get_user():
return "user"
include_router(users)
for method, path, tags, fn in app_routes:
print(method, path, "| tags:", tags)
Попробуй сам ▶ Видно, как префикс /users приклеился к каждому пути при подключении роутера.
Частые ошибки
Первая — задавать префикс с завершающим слешем (prefix="/users/"), получая двойные слеши в путях. Вторая — дублировать префикс и в роутере, и в путях маршрутов. Третья — городить циклические импорты, когда модули роутеров импортируют друг друга; лучше держать зависимости направленными. Четвёртая — забывать про теги, из-за чего документация превращается в одну плоскую свалку эндпоинтов.
Best practices
- Один роутер на ресурс/домен (
users,orders,auth). - Префикс — без завершающего слеша; пути внутри начинаются с
/. - Назначайте теги — документация станет читаемой и навигируемой.
- Общие зависимости (авторизация) навешивайте на уровне роутера, а не повторяйте в каждом обработчике.
Версионирование API через роутеры
Роутеры отлично решают задачу версионирования. Когда контракт API меняется несовместимо, нельзя ломать старых клиентов — заводят новую версию пути, обычно /api/v1 и /api/v2. С APIRouter это делается естественно: каждая версия — свой набор роутеров со своим префиксом, подключаемый к приложению независимо. Старые и новые эндпоинты сосуществуют, а общую логику выносят в зависимости, чтобы не дублировать. Тот же приём помогает разделять публичное и внутреннее API, накладывая на внутренний роутер дополнительные зависимости авторизации. Главная мысль: роутер — это не просто способ разбить файлы, а единица композиции, на уровне которой удобно навешивать префиксы, теги, зависимости и версии. Чем раньше вы начнёте мыслить роутерами, тем легче проект будет масштабироваться без болезненных переписываний структуры.
Итог: APIRouter — модульная единица маршрутов; include_router приклеивает префикс, добавляет теги и общие зависимости и сливает маршруты в приложение. Это основа масштабируемой структуры проекта.