Документация в FastAPI: разбор кейса с разросшимся API
Когда эндпоинтов становится за сотню, автогенерированная документация FastAPI превращается из удобного инструмента в неуправляемую стену текстов. Разбираем, как вернуть ей читаемость: теги, роутеры, кастомизация OpenAPI-схемы и типичные ловушки.

Проблема: Swagger UI, который никто не читает
FastAPI из коробки генерирует интерактивную документацию на базе OpenAPI 3.1 — достаточно запустить приложение и открыть /docs. На старте проекта это выглядит идеально: десять-двадцать эндпоинтов, аккуратный список, понятные модели. Через полгода эндпоинтов сто сорок, половина — внутренние, часть — deprecated, а Swagger UI превращается в бесконечную ленту, которую фронтенд-разработчик пролистывает с ужасом.
Ситуация типичная для команд, которые стартовали быстро и не заложили структуру документации заранее. Ниже — разбор конкретного кейса: Python-сервис на FastAPI 0.115.x, ~120 эндпоинтов, три команды-потребителя (фронт, мобильное приложение, внутренний бэкофис). Показываем, что ломается и как чинить.
Что ломается первым
Автогенерация документации в FastAPI опирается на Pydantic-модели и декораторы маршрутов. Пока проект маленький, всё работает без усилий. С ростом появляются конкретные симптомы.
- Плоский список эндпоинтов. Без тегов все маршруты идут сплошной лентой. Swagger UI не умеет группировать то, что не размечено.
- Дублирование моделей. Два разных эндпоинта возвращают похожие, но не идентичные схемы —
UserResponseиUserShortResponse. В секции Schemas накапливаются десятки моделей, отличающихся одним полем. - Внутренние эндпоинты видны снаружи. Маршруты для healthcheck, метрик, дебаг-ручек отображаются рядом с публичным API.
- Пустые описания. Без docstring и параметра
summaryв декораторе Swagger показывает имя функции —get_user_by_id_v2— и ничего больше. - Невалидные примеры. Поля Pydantic-моделей без
examplesилиjson_schema_extraполучают дефолтные значения, которые не проходят собственную валидацию модели.
Структура: роутеры и теги
FastAPI использует объект APIRouter для группировки маршрутов. Каждый роутер может нести свой тег, префикс и набор зависимостей. Это основной инструмент для организации документации — не дополнительный, а первичный.
Разделение по роутерам
# routers/users.py — FastAPI 0.115.x
from fastapi import APIRouter
router = APIRouter(
prefix="/users",
tags=["Пользователи"],
)
@router.get(
"/{user_id}",
summary="Получить пользователя по ID",
description="Возвращает публичный профиль. Для внутренних полей используйте /admin/users/{user_id}.",
)
async def get_user(user_id: int):
...
Ключевой момент: тег задаётся на уровне роутера, а не на каждом маршруте отдельно. Если тег указан и там, и там, Swagger покажет эндпоинт в обеих группах — иногда это намеренно, но чаще это мусор.
Порядок тегов в Swagger UI
Swagger отображает группы в том порядке, в котором теги впервые встретились в OpenAPI-схеме. FastAPI позволяет задать порядок явно через параметр openapi_tags при создании приложения:
# main.py
from fastapi import FastAPI
tags_metadata = [
{"name": "Пользователи", "description": "CRUD профилей"},
{"name": "Заказы", "description": "Создание и отслеживание заказов"},
{"name": "Админ", "description": "Внутренние ручки бэкофиса"},
]
app = FastAPI(
title="My Service API",
version="2.4.0",
openapi_tags=tags_metadata,
)
Если тег есть в openapi_tags, но ни один маршрут его не использует, он просто не отобразится — это безопасно.
Скрытие внутренних эндпоинтов
Два подхода, и оба уместны в разных ситуациях.
| Подход | Как работает | Цена |
|---|---|---|
Параметр include_in_schema=False | Эндпоинт работает, но исчезает из OpenAPI-схемы и Swagger UI | Просто, но если эндпоинт нужен другой команде через схему — они его не увидят |
| Отдельное приложение FastAPI для внутренних маршрутов | Два экземпляра FastAPI, смонтированных на разные префиксы или порты | Сложнее деплой, зато у каждого приложения своя /docs и свои потребители |
Первый подход — для единичных ручек (healthcheck, debug). Второй — если внутренних маршрутов десятки и у них своя аудитория.
# Скрытие конкретного эндпоинта
@router.get("/internal/metrics", include_in_schema=False)
async def metrics():
...
Модели: примеры и наследование
Pydantic v2 (используется в FastAPI начиная с 0.100.0) изменил способ задания примеров. Атрибут schema_extra из v1 заменён на model_config с ключом json_schema_extra.
Примеры, которые проходят валидацию
# schemas/user.py — Pydantic v2
from pydantic import BaseModel, EmailStr, model_config
class UserResponse(BaseModel):
model_config = {
"json_schema_extra": {
"examples": [
{
"id": 42,
"email": "reader@example.com",
"is_active": True,
}
]
}
}
id: int
email: EmailStr
is_active: bool
Частая ошибка: пример содержит поле, которого нет в модели, или значение не того типа. Swagger покажет пример как есть, но при попытке использовать кнопку «Try it out» запрос может упасть — и доверие к документации обнулится.
Борьба с дублированием
Когда UserResponse и UserShortResponse отличаются одним полем, создавать две независимые модели — путь к рассинхронизации. Pydantic v2 поддерживает наследование, но для OpenAPI-схемы удобнее использовать явную вложенность или общую базовую модель:
class UserBase(BaseModel):
id: int
email: EmailStr
class UserShortResponse(UserBase):
"""Для списков — без тяжёлых полей."""
pass
class UserFullResponse(UserBase):
"""Полный профиль."""
is_active: bool
created_at: datetime
orders_count: int
В секции Schemas Swagger покажет три модели, но их связь будет очевидна по именам и набору полей.
Документация — это не бонус, а интерфейс. Если потребитель API тратит двадцать минут на поиск нужного эндпоинта, проблема не в потребителе.
Кастомизация OpenAPI-схемы
FastAPI позволяет подменить генерацию OpenAPI-схемы целиком через переопределение метода app.openapi(). Это мощный, но опасный инструмент: легко сломать совместимость с кодогенераторами клиентов.
Типичный сценарий, где это оправдано: нужно добавить в схему кастомные расширения (поля с префиксом x-), которые использует ваш внутренний портал документации или API-гейтвей.
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
schema = get_openapi(
title=app.title,
version=app.version,
routes=app.routes,
tags=tags_metadata,
)
# Добавляем кастомное расширение для API-гейтвея
for path in schema.get("paths", {}).values():
for method in path.values():
if "Админ" in method.get("tags", []):
method["x-internal"] = True
app.openapi_schema = schema
return schema
app.openapi = custom_openapi
Если ваш случай — просто навести порядок в группировке и описаниях, переопределять openapi() не нужно. Хватит тегов, summary, description и грамотных моделей.
Чек-лист: что проверить прямо сейчас
- Откройте
/docsи посчитайте группы тегов. Если всё в одной — начните сAPIRouterи тегов. - Проверьте, есть ли
summaryу каждого эндпоинта. Имя функции — не замена описанию. - Найдите эндпоинты без
response_model. Без него Swagger не покажет схему ответа — потребитель угадывает формат. - Откройте секцию Schemas внизу Swagger UI. Если моделей вдвое больше, чем вы ожидали, — ищите дублирование.
- Попробуйте «Try it out» на трёх-четырёх ключевых ручках. Если пример из документации не проходит валидацию — это сломанная документация.
OpenAPI — спецификация для описания HTTP API в машиночитаемом формате (JSON/YAML). FastAPI генерирует OpenAPI-схему автоматически на основе типов Python и Pydantic-моделей. Swagger UI — один из визуализаторов этой схемы; альтернатива — ReDoc, доступный в FastAPI по адресу /redoc.
Что дальше
Структурированная документация — это фундамент, но не потолок. Следующий шаг — генерация клиентских SDK из OpenAPI-схемы (openapi-generator, openapi-typescript). Если схема чистая, клиент генерируется за минуту. Если в схеме мусор — мусор окажется в сгенерированном коде, и каждый потребитель будет обходить его по-своему.
Отдельная тема — версионирование API и отражение версий в документации. Здесь мы её не разворачиваем, но если у вас уже есть /v1 и /v2 на одном приложении, стоит задуматься о раздельных OpenAPI-схемах для каждой версии.
Измеримый показатель: время, которое новый разработчик тратит на первый успешный запрос к вашему API. Если это больше пятнадцати минут — документация не справляется.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: FastAPI — официальная документация: Bigger Applications — Multiple Files, FastAPI — официальная документация: Metadata and Docs URLs
Частые вопросы
Чем /docs отличается от /redoc в FastAPI?
Можно ли полностью отключить автогенерацию документации?
Как быть, если часть эндпоинтов на Pydantic v1, а часть на v2?
Swagger показывает не ту модель в ответе — как отладить?
Замедляет ли генерация OpenAPI-схемы работу сервиса в проде?
Что если я обновлю FastAPI, а схема изменится?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.