lenec ru

← все посты

OpenAPI 3.1 vs 3.0: что реально поменялось и стоит ли мигрировать

16K

OpenAPI 3.1 вышел в 2021, и до сих пор половина команд сидит на 3.0, потому что «там же всё работает». Работает, да. Только когда надо описать поле, которое одновременно строка и null, или сослаться на JSON Schema из внешнего файла, начинаются костыли. 3.1 решает большинство таких проблем, но миграция не бесплатна: tooling догоняет неравномерно, кодогенераторы ломаются на webhooks, и пара любимых хаков из 3.0 перестаёт работать.

Разберу по пунктам, что реально поменялось между версиями, где 3.1 лучше, а где можно подождать. Без «революционных улучшений» и «следующего поколения» — конкретные изменения и решение, мигрировать или нет.

Главное: 3.1 — это надмножество JSON Schema 2020-12

В 3.0 схема для тел запросов и ответов формально похожа на JSON Schema, но это собственный диалект OpenAPI с заметными отличиями. nullable: true, example в единственном числе, отсутствие const, ограниченная поддержка $ref. Из-за этого валидаторы JSON Schema не работали с OpenAPI-схемами напрямую — нужны были адаптеры.

В 3.1 схемы — это полноценный JSON Schema 2020-12. Что это значит на практике:

  • Можно брать любую готовую JSON Schema (например, из schemas.json-schema.org) и подставлять в OpenAPI без переделки.
  • Валидатор JSON Schema, который у тебя уже есть в коде, работает на тех же схемах, что и документация.
  • Появились const, if/then/else, $dynamicRef, unevaluatedProperties — то, что в 3.0 либо отсутствовало, либо называлось по-другому.

Nullable: было — стало

В 3.0 nullable-поле описывалось так:

properties:
  middleName:
    type: string
    nullable: true

В 3.1 атрибут nullable убрали. Вместо него — массив типов, как в JSON Schema:

properties:
  middleName:
    type: [string, "null"]

Кажется мелочью, но «нул» в кавычках забывают постоянно. Без них YAML-парсер превратит значение в null, а не в строку "null", и валидация сломается с непрозрачной ошибкой. Лично для меня это самый частый баг при миграции.

Если у тебя в спеке десятки nullable: true, есть готовые скрипты — например, swagger2openapi с флагом --targetVersion 3.1.0 переписывает их автоматически.

Examples: множественное число

В 3.0 на уровне схемы было одно example, и его дублировали в examples на уровне media-type:

schema:
  type: object
  properties:
    name:
      type: string
      example: Alice

В 3.1 на уровне схемы появилось examples (массив) — синхронно с JSON Schema:

schema:
  type: object
  properties:
    name:
      type: string
      examples: [Alice, Bob]

Старое example в 3.1 deprecated, но работает. Если миграция через автоматический конвертер — он обычно оставляет example как есть, и в спеке начинают сосуществовать оба варианта. По возможности приведи к одному, иначе ревьюверы регулярно будут спорить, какой использовать в новых эндпоинтах.

Webhooks как отдельная сущность

В 3.0 описать webhook (когда твой сервис стучится в URL клиента) можно было только через callbacks внутри какого-то эндпоинта. Если webhook не привязан к конкретному запросу — например, «мы пушим события order.created, когда хотим» — приходилось придумывать фейковый эндпоинт «subscribe» только ради callback-блока.

В 3.1 webhooks — это корневая секция:

webhooks:
  orderCreated:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderEvent'
      responses:
        '200':
          description: Webhook received

Этого достаточно, чтобы Stoplight/Redoc отрисовали отдельный раздел с webhook-эндпоинтами, а кодогенераторы — собрали серверную «ручку» для приёма пушей. До 3.1 я оформляла такие интеграции в отдельной markdown-странице, потому что в спеку нормально не клалось.

Files: $ref в любую сторону

В 3.0 разрешение $ref между файлами работало, но с оговорками: ссылаться можно было только на JSON Schema-объекты, и инструменты по-разному обрабатывали относительные пути. В 3.1 поведение $ref стандартизовано через JSON Reference 2020-12: можно ссылаться на любой узел любого файла, поддерживается $id для bundle.

paths:
  /users:
    get:
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: './schemas/user.yaml#/User'

Это удобно для команд, где спека разбита на десятки файлов: один путь, одна схема, один $ref. Bundle-инструменты (redocly bundle, swagger-cli bundle) собирают всё в один файл для деплоя.

Mutual exclusion: oneOf vs anyOf

В 3.0 различие oneOf/anyOf часто игнорировали — половина инструментов валидировала их одинаково. В 3.1 семантика жёсткая: oneOf требует совпадения ровно с одной схемой, anyOf — с одной или больше. Если у тебя был трюк «положу две похожие схемы в oneOf, авось пройдёт» — в 3.1 он перестанет работать на строгих валидаторах.

Mutual TLS и новые security-схемы

В 3.1 добавили mutualTLS как тип securityScheme:

components:
  securitySchemes:
    mtls:
      type: mutualTLS
      description: Client certificate required

В 3.0 mTLS описывали через комментарии или OpenID Connect — некрасиво. Если у тебя b2b-интеграции с сертификатами, это маленькая, но приятная мелочь.

Что сломается при миграции

Самые частые проблемы, на которые натыкаются команды:

  • Кодогенераторы. На середину 2026 openapi-generator поддерживает 3.1 не во всех target-языках одинаково. Проверь свой генератор до миграции, иначе после конвертации перестанет собираться SDK.
  • Mock-серверы. Prism, Stoplight Studio — поддержка 3.1 есть, но местами сырая. Особенно в части новых JSON Schema-конструкций (unevaluatedProperties, $dynamicRef).
  • Type generators. openapi-typescript и аналоги — обычно ок, но проверь, что type: [string, "null"] у них превращается в string | null, а не в string | "null".
  • Внутренние тулзы. Если у вас есть свой парсер спеки (для линтинга, для рендера админки), он почти наверняка завязан на 3.0 и не поймёт webhooks или массив типов.

Поэтому миграция — это не swagger2openapi в CI и пинок в прод, а отдельный спринт с прогонкой всех зависимых инструментов.

Когда мигрировать прямо сейчас

Стоит, если:

  • Описываешь webhooks и заметно страдаешь от их отсутствия в 3.0.
  • В коде уже используется JSON Schema 2020-12 — тогда в 3.1 пропадает дублирование схем.
  • API на этапе разработки, нет внешних клиентов, которые ломаются от изменений.
  • В команде есть кто-то, кто прогонит зависимый tooling и почистит хвосты.

Когда лучше остаться на 3.0

Не торопись, если:

  • SDK генерируются в Java/Go/.NET через старый кодогенератор и собираются в CI каждого релиза.
  • Спека — публичный контракт со множеством клиентов, сторонних SDK, gateway-ями. Любой риск ломучести трогать дороже.
  • Твой основной инструмент рендера/мок-сервера ещё не поддерживает 3.1 (бывает, особенно у внутренних форков SwaggerUI).

Рецепт миграции, если решились

  1. Сделай ветку, прогони swagger2openapi --targetVersion 3.1.0 на корневом файле.
  2. Открой diff, прочитай каждое изменение глазами. Особенно — преобразование nullable и example/examples.
  3. Прогоняй спеку через spectral lint или redocly lint с правилами для 3.1.
  4. Сгенерируй SDK всеми генераторами, которые используешь. Собери, запусти тесты SDK.
  5. Прогоняй mock-сервер на тестовых запросах и проверь, что валидируется так, как ожидаешь.
  6. Отдельно проверь oneOf/anyOf — если строгая валидация поломалась, чини схемы, а не возвращайся на 3.0.
  7. Только после этого мерж в main и публикация документации.

Я обычно закладываю на такую миграцию неделю на средних спеках (50–100 эндпоинтов). На больших — две, потому что половина времени уходит не на саму спеку, а на смежные тулзы.

Что в 3.1 не поменялось

Чтобы не было ложных ожиданий: 3.1 не вводит новых способов описывать асинхронные API (это AsyncAPI), GraphQL, gRPC. Не появилось встроенной поддержки версионирования API. Структура paths/components/servers та же. Если у тебя проблема с одной из этих областей, версия OpenAPI не поможет — нужен другой инструмент или соглашение в команде.

Итог по решению

Если новый проект — пиши сразу на 3.1, никаких причин начинать на 3.0 в 2026 нет. Если зрелая спека с экосистемой клиентов — мигрируй, но запланированно: проверь tooling, выдели спринт, прогони regression-тесты SDK. Главное преимущество 3.1 — единая схема валидации для кода и доки, поэтому максимум выигрыша получают команды, которые уже используют JSON Schema в рантайме.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.