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

Зачем трогать сгенерированную схему
FastAPI строит OpenAPI-документ на лету: собирает метаданные маршрутов, типы Pydantic-моделей и параметры запросов, а затем отдаёт готовый JSON по адресу /openapi.json. Для прототипа этого достаточно. Для внешнего API, которым пользуется мобильная команда или партнёр, — нет. Типичные жалобы: все эндпоинты свалены в один список, описания пустые, примеры запросов отсутствуют, а в схему попали служебные маршруты вроде /health или внутренних webhook-ов.
Ниже — конкретные приёмы, которые закрывают эти проблемы. Всё проверено на FastAPI 0.111.x и Pydantic v2. Если вы работаете с Pydantic v1, синтаксис конфигурации моделей отличается: сверяйтесь с гайдом по миграции Pydantic.
Метаданные приложения
Самое быстрое улучшение — заполнить аргументы конструктора FastAPI(). Они напрямую попадают в корневой объект OpenAPI-спецификации.
# app.py — FastAPI 0.111.x, Python 3.11+
from fastapi import FastAPI
app = FastAPI(
title="Catalog API",
version="1.2.0",
summary="API каталога товаров для мобильного приложения",
description=(
"Позволяет создавать, редактировать и фильтровать товары. "
"Аутентификация — Bearer-токен в заголовке Authorization."
),
terms_of_service="https://example.com/terms",
contact={"name": "Backend Team", "email": "backend@example.com"},
license_info={"name": "MIT", "url": "https://opensource.org/licenses/MIT"},
)
После перезапуска Swagger UI покажет заполненную шапку вместо дефолтного «FastAPI». Параметр summary появился в FastAPI 0.99 — в более ранних версиях его нет.
Группировка через теги
Без тегов Swagger UI выстраивает все маршруты плоским списком. Теги решают проблему, но полезны только если у каждого тега есть описание и порядок осмыслен.
tags_metadata = [
{
"name": "products",
"description": "CRUD товаров. Требует роли `editor` для записи.",
},
{
"name": "orders",
"description": "Оформление и отслеживание заказов.",
},
]
app = FastAPI(
title="Catalog API",
version="1.2.0",
openapi_tags=tags_metadata,
)
@app.get("/products", tags=["products"])
async def list_products():
...
Swagger UI отображает теги ровно в том порядке, в котором они перечислены в openapi_tags. Поставьте самые частые разделы первыми — потребители API скажут спасибо.
Описания и примеры на уровне эндпоинта
Docstring и параметры декоратора
FastAPI берёт summary из первой строки docstring функции, а description — из остальных. Если это неудобно, передавайте их явно в декораторе:
@app.post(
"/products",
tags=["products"],
summary="Создать товар",
description="Принимает JSON с полями name, price, category. Возвращает созданный объект с id.",
response_description="Созданный товар",
status_code=201,
responses={
409: {"description": "Товар с таким SKU уже существует"},
},
)
async def create_product(product: ProductIn) -> ProductOut:
...
Словарь responses дополняет, а не заменяет автоматически сгенерированный 2xx-ответ. Через него удобно документировать ошибки, которые роутер реально возвращает.
Примеры в Pydantic-моделях
Пустая схема без примеров заставляет потребителя гадать, какой формат ожидается. Pydantic v2 принимает примеры через model_config и через Field:
from pydantic import BaseModel, Field
class ProductIn(BaseModel):
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Кроссовки Air Max",
"price": 12990,
"category": "shoes",
}
]
}
}
name: str = Field(min_length=1, max_length=200, description="Название товара")
price: int = Field(gt=0, description="Цена в рублях без копеек")
category: str = Field(description="Slug категории, например 'shoes'")
Swagger UI подставит пример в форму «Try it out», что снижает количество вопросов от фронтенд-команды примерно до нуля.
Скрытие маршрутов
Служебные эндпоинты — healthcheck, метрики, внутренние вебхуки — не должны попадать во внешнюю документацию. Два способа:
- Параметр
include_in_schema=Falseв декораторе маршрута — самый простой вариант. - Кастомная функция генерации — если нужно скрыть целые роутеры или отфильтровать маршруты по условию (см. раздел ниже).
@app.get("/health", include_in_schema=False)
async def healthcheck():
return {"status": "ok"}
Полная замена функции генерации
Когда точечных правок недостаточно, можно заменить всю функцию, которая строит OpenAPI-схему. FastAPI вызывает app.openapi() при первом запросе к /openapi.json и кеширует результат.
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,
summary=app.summary,
description=app.description,
routes=app.routes,
tags=tags_metadata,
)
# Удаляем 422-ответ из всех эндпоинтов — клиентам он не нужен
for path_data in schema["paths"].values():
for method_data in path_data.values():
method_data.get("responses", {}).pop("422", None)
app.openapi_schema = schema
return schema
app.openapi = custom_openapi
Удаление 422-ответа — самый частый запрос при кастомизации. FastAPI добавляет его ко всем маршрутам автоматически, но для внешних потребителей он бесполезен: клиент не может повлиять на формат ValidationError, а тип ошибки документируется лучше через конкретные 4xx-коды.
Внутри custom_openapi вы работаете с обычным словарём, который соответствует OpenAPI 3.1 (или 3.0, в зависимости от версии FastAPI). Можно добавить серверы, security-схемы, удалить пути по префиксу — любая мутация допустима, пока результат остаётся валидной спецификацией.
Сравнение приёмов
| Задача | Приём | Где менять | Когда ломается |
|---|---|---|---|
| Заполнить шапку документации | Аргументы FastAPI() | Конструктор приложения | Не ломается — чистая метадата |
| Сгруппировать эндпоинты | openapi_tags + tags в декораторах | Конструктор + каждый роутер | Забытый тег — маршрут уходит в «default» |
| Добавить примеры | json_schema_extra / Field | Pydantic-модели | Пример рассинхронизировался с валидацией |
| Скрыть маршрут | include_in_schema=False | Декоратор маршрута | Нужно помнить про каждый новый служебный роут |
| Массовая правка схемы | Замена app.openapi | Отдельная функция | Мутации ломают спецификацию, если не проверять результат |
Типичные ошибки
- Примеры не соответствуют валидации. Поле
priceсgt=0, а в примере стоит0. Swagger UI покажет пример, но «Try it out» вернёт 422. Держите примеры вjson_schema_extra— тогда они лежат рядом с правилами валидации и проще заметить расхождение. - Кеш
openapi_schemaне сбрасывается. Если вы меняете маршруты динамически (плагины, мультитенант), закешированная схема не обновится. В таком сценарии уберите строкуapp.openapi_schema = schemaили предусмотрите инвалидацию. - Дублирование моделей. Две Pydantic-модели с одинаковым именем, но разными полями дают коллизию в
#/components/schemas. FastAPI дописывает суффикс, но результат выглядит какProductInиProductIn1— непонятно потребителю. Давайте моделям уникальные имена или задавайтеmodel_config = {"title": "CreateProductRequest"}. - Забытый
response_model. Без него FastAPI не генерирует схему ответа — в документации будет пустой 200. Либо аннотируйте возвращаемый тип (-> ProductOut), либо укажитеresponse_model=ProductOutв декораторе.
Сноска: OpenAPI — спецификация для описания HTTP API, ранее известная как Swagger. Текущая версия — 3.1.0, совместимая с JSON Schema. FastAPI с версии 0.99 генерирует схему в формате OpenAPI 3.1, более ранние версии — 3.0.2.
Что делать дальше
После кастомизации схемы стоит включить её валидацию в CI. Утилита openapi-spec-validator (pip-пакет) проверяет, что результат /openapi.json остаётся корректной спецификацией после любых мутаций. Подключить её к pytest — дело одного теста. Если вы отдаёте схему внешним командам, зафиксируйте её как артефакт и сравнивайте диффы между релизами: так сломанный контракт будет заметен до деплоя, а не после жалобы от мобильной команды.
Карточки для маркетплейсов: Таблица товаров и папка фотографий на входе — комплект слайдов и описаний на выходе, по требованиям Wildberries и Ozon.
Спросить за 5 ₽Источники: FastAPI — Extending OpenAPI (официальная документация), OpenAPI Specification 3.1.0
Частые вопросы
Можно ли сгенерировать отдельные OpenAPI-схемы для внутренних и внешних потребителей?
Почему FastAPI добавляет ответ 422 ко всем эндпоинтам?
Как добавить Bearer-аутентификацию в Swagger UI через OpenAPI-схему?
Что будет, если обновить FastAPI и кастомная функция openapi сломается?
Как быть, если у меня Pydantic v1 и json_schema_extra не работает?
Схема стала большой, Swagger UI тормозит — что делать?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.