Документация: OpenAPI и Swagger

Почему документация — не приятный бонус, а контракт, без которого API не существует для остального мира.

OpenAPI Specification — это машинночитаемое описание REST API в формате YAML или JSON: какие есть эндпоинты, методы, параметры, тела запросов и форматы ответов.

Можно написать идеальный API: правильные существительные в URI, корректные статусы, аккуратные ошибки. Но если о нём знаете только вы, его не существует. Другой разработчик в команде не угадает, что POST /orders требует поле customerId, а в ответ может прийти 409 Conflict. Внешний интегратор не поймёт, как выглядит объект заказа. Документация — это контракт: формальное обещание, что API ведёт себя так, как написано. И как у любого контракта, у неё должна быть единая, проверяемая форма.

Зачем нужна машинночитаемая спецификация

Текстовая документация в вики устаревает в тот же день, когда её написали. Кто-то меняет код, забывает поправить страницу — и вот уже описание врёт. OpenAPI решает это иначе: спецификация — структурированный документ, который понимают не только люди, но и инструменты. Из одного файла можно автоматически сгенерировать красивую страницу документации, поднять мок-сервер, создать клиентскую библиотеку на любом языке и проверить, что реальные ответы сервера соответствуют обещанному формату.

Исторически проект назывался Swagger. В 2015 году спецификацию передали в OpenAPI Initiative, и сам язык описания переименовали в OpenAPI Specification (OAS). Имя «Swagger» осталось за линейкой инструментов: Swagger UI, Swagger Editor, Swagger Codegen. Поэтому фразы «сваггер нашего API» и «openapi-спека» обычно означают одно и то же.

Структура спецификации

Файл OpenAPI состоит из нескольких ключевых разделов. На верхнем уровне — метаданные (info), список серверов (servers), описание путей (paths) и переиспользуемые компоненты (components). Внутри каждого пути перечислены HTTP-методы, а внутри метода — параметры, тело запроса и возможные ответы.

openapi: 3.0.3
info:
  title: Orders API
  version: 1.0.0
servers:
  - url: https://api.example.com/v1
paths:
  /orders/{orderId}:
    get:
      summary: Получить заказ по идентификатору
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Заказ найден
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '404':
          description: Заказ не найден
components:
  schemas:
    Order:
      type: object
      required: [id, status, total]
      properties:
        id:
          type: integer
        status:
          type: string
          enum: [new, paid, shipped]
        total:
          type: number

Разберём по частям. В paths описан эндпоинт /orders/{orderId} с методом get. parameters объявляет параметр пути orderId типа integer. responses перечисляет, что может вернуть сервер: 200 с телом-объектом Order или 404. Само описание объекта вынесено в components/schemas и подключается ссылкой $ref. Это даёт переиспользование: одну схему Order можно ссылаться из десятка эндпоинтов, не дублируя поля.

requestBody для записи

Для методов POST и PUT добавляется секция requestBody — она описывает тело запроса так же, как ответ описывает тело отдачи:

    post:
      summary: Создать заказ
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderInput'
      responses:
        '201':
          description: Заказ создан

Swagger UI и Redoc

Сам по себе YAML-файл читать неудобно. Поэтому поверх него запускают рендереры. Swagger UI превращает спецификацию в интерактивную страницу, где каждый эндпоинт можно развернуть и нажать «Try it out» — отправить настоящий запрос прямо из браузера и увидеть ответ. Redoc делает упор на чистую, читаемую документацию в три колонки: навигация, описание, примеры. Оба берут на вход один и тот же файл openapi.yaml — выбор между ними чисто эстетический.

Design-first против code-first

Есть два подхода к тому, откуда берётся спецификация. При code-first (генерация из кода) вы пишете контроллеры, развешиваете аннотации, а фреймворк собирает openapi.yaml автоматически. Быстро на старте, но спека отстаёт от обсуждения дизайна — её видно только после написания кода.

При design-first (он же spec-first) спецификацию пишут первой, до единой строчки реализации. Команда обсуждает контракт, фронтенд и бэкенд договариваются о формате, затем параллельно: бэкенд реализует, фронтенд работает по моку. Спека становится источником истины, а не побочным продуктом.

КритерийCode-firstDesign-first
Стартбыстреемедленнее
Источник истиныкодспецификация
Параллельная работа командсложнеепроще (мок по спеке)
Риск рассинхронанижевыше, нужна валидация

Что даёт спецификация

Готовый openapi.yaml — это не документ, а актив, который умножается инструментами:

  • Моки. Мок-сервер (например, Prism) поднимает фейковый API по спеке — фронтенд начинает работу, пока бэкенд ещё пишется.
  • Кодген клиентов. Из спеки генерируются типизированные SDK на TypeScript, Python, Go — без ручного написания обёрток над HTTP.
  • Валидация. Контрактные тесты сверяют реальные ответы сервера со схемами из спеки — рассинхрон ловится автоматически.

Как работает под капотом

OpenAPI опирается на JSON Schema — стандарт описания структуры данных. Когда вы пишете type: object с properties и required, вы фактически декларируете JSON Schema для тела. Валидатор берёт реальный JSON-ответ и рекурсивно проверяет: есть ли обязательные поля, совпадают ли типы, входит ли значение в enum. Ссылки $ref — это указатели внутри документа (или на внешний файл), которые инструмент разворачивает перед обработкой, собирая плоское дерево схем. Именно поэтому спека машинночитаема: за каждым словом стоит формальная грамматика, а не свободный текст.

Частые ошибки

  • Документация отдельно от кода. Спека лежит в вики и не проверяется в CI — она устаревает молча. Храните openapi.yaml в репозитории и валидируйте.
  • Дублирование вместо $ref. Описывать объект Order заново в каждом ответе — путь к рассинхрону. Выносите в components/schemas.
  • Только happy path. Описан 200, но забыты 400, 404, 401. Клиент не знает, как обрабатывать ошибки.
  • Путаница версий. Смешивание Swagger 2.0 и OpenAPI 3.x синтаксиса — инструменты падают. Зафиксируйте версию в поле openapi.

Итоги

  • OpenAPI (бывший Swagger) — машинночитаемый контракт REST API в YAML/JSON.
  • Ключевые разделы: paths, методы, parameters, requestBody, responses, components/schemas.
  • Swagger UI даёт интерактивную страницу, Redoc — чистую читаемую документацию из той же спеки.
  • Design-first делает спеку источником истины; code-first быстрее, но рискует рассинхроном.
  • Из спеки рождаются моки, типизированные клиенты и контрактная валидация — документация становится активом.
Проверьте себя
1. Где в OpenAPI-спецификации принято описывать переиспользуемую структуру объекта, чтобы ссылаться на неё из нескольких эндпоинтов?
AВ разделе paths рядом с каждым методом
BВ components/schemas со ссылкой через $ref
CВ info
DВ servers
2. Чем подход design-first отличается от code-first?
AСпецификацию пишут первой, до реализации, и она становится источником истины
BКод пишут на другом языке
CДокументацию не пишут вообще
DИспользуют только JSON, а не YAML
3. Что НЕ относится к Swagger как набору инструментов после переименования спецификации в OpenAPI?
ASwagger UI
BSwagger Editor
CSwagger Codegen
DСам язык описания API