OpenAPI — это формат описания HTTP API в виде обычного файла YAML или JSON. В нём перечислено, какие у API есть адреса, какие методы они принимают, какие поля уходят в запросе и что возвращается в ответе. Файл читает и человек, и программа: по нему генерируют документацию, клиентские библиотеки и заглушки для тестов.

Формальное название — OpenAPI Specification, сокращённо OAS. Развивает её OpenAPI Initiative, проект с открытым управлением под эгидой Linux Foundation.

Дальше — как устроен документ, чем OpenAPI отличается от Swagger, что изменилось в версиях 3.1 и 3.2 и почему главное обещание этого формата — «документация никогда не устареет» — само по себе не работает.

Как выглядит документ OpenAPI

Минимальный рабочий файл короче, чем кажется:

openapi: 3.1.0

info:
  title: Ссылки
  version: "1.0"

paths:
  /links:
    post:
      summary: Создать короткую ссылку
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
      responses:
        '201':
          description: Ссылка создана

Обязательных полей верхнего уровня всего два — openapi и info; кроме них документ должен содержать хотя бы одно из paths, components или webhooks. Так это описано в спецификации OAS 3.1.1.

Здесь же прячется путаница, которую я встречал в чужих файлах не раз: openapi и info.version — это разные вещи. Первое — версия самого формата, по которой инструменты понимают, как читать документ. Второе — версия вашего API. Спецификация говорит об этом прямо, но интуиция подсказывает обратное, и в результате появляются файлы, где openapi: 1.0.

Крупные спецификации обычно разносят по файлам через $ref: корневой документ ссылается на ./paths/links.yaml, тот — на ./schemas/link.yaml. Это удобно и создаёт одну неочевидную проблему, к которой я вернусь в конце.

OpenAPI и Swagger — это не одно и то же

Самая частая путаница в теме.

Что это Кто отвечает
OpenAPI стандарт, описание формата OpenAPI Initiative, Linux Foundation
Swagger набор инструментов, которые этот формат читают SmartBear

История объясняет, откуда взялась каша. Сначала Swagger был одновременно и спецификацией, и инструментами. Потом спецификацию передали в OpenAPI Initiative, и она стала называться OpenAPI Specification, а имя Swagger осталось за инструментами — Swagger UI, Swagger Editor и прочими.

Практический вывод: «у нас Swagger» обычно означает «у нас есть OpenAPI-файл, и мы показываем его через Swagger UI». Файл при этом можно открыть чем угодно другим — Redoc, Scalar, генераторами клиентов. Инструмент и формат не связаны намертво.

Версии: 3.0, 3.1 и 3.2

Различия между ветками — не косметика.

3.0 — самая распространённая версия. Её ограничение в том, что схемы данных в ней похожи на JSON Schema, но совместимы не полностью, и это регулярно ломает совместную работу инструментов.

3.1 закрыла этот разрыв: объект Schema стал надмножеством JSON Schema Draft 2020-12. Если вы уже описываете данные через JSON Schema, в 3.1 их можно переиспользовать без переписывания. Последняя редакция — 3.1.1, опубликована 24 октября 2024 года.

3.2 вышла в сентябре 2025 года. Из анонса OpenAPI Initiative заметнее всего:

  • HTTP-метод query — для идемпотентных запросов, где параметры не влезают в строку адреса;
  • additionalOperations для нестандартных методов;
  • потоковые форматы: Server-Sent Events, JSON Lines, JSON-последовательности;
  • теги научились иерархии — появились summary, parent и kind;
  • OAuth 2.0 device flow для устройств без удобного ввода.

Выбирать версию стоит по инструментам, а не по номеру. Поддержка 3.1 в экосистеме хорошая, 3.2 новее и местами ещё догоняется. Наша собственная спецификация объявлена как 3.1.0.

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

Один файл закрывает сразу несколько задач:

  • интерактивная документация — Swagger UI или Redoc строят страницу прямо из файла, включая форму «попробовать запрос»;
  • клиентские библиотеки — генераторы собирают SDK под нужный язык, не заставляя писать обёртки руками;
  • заглушки — мок-сервер поднимается по описанию до того, как готов бэкенд, и фронтенд не ждёт;
  • проверка контракта — тесты сверяют реальные ответы со схемами и ловят момент, когда ответ перестал совпадать с описанием.

Именно из этого списка вырастает главный аргумент в пользу OpenAPI: документация перестаёт быть отдельным текстом, который кто-то забывает обновить.

Аргумент верный. Но он верен не сам по себе.

Спецификацию ничто не связывает с кодом

Это то, о чём почти не пишут в статьях «что такое OpenAPI», и то, обо что спотыкаются в реальности.

Файл спецификации — просто файл. Роутер о нём не знает. Контроллер о нём не знает. Вы можете удалить эндпоинт, добавить новый, поменять обязательное поле на необязательное — и ни один тест не упадёт. Документация не «синхронизируется автоматически», она синхронизируется ровно настолько, насколько кто-то помнит её править.

Разъезжается это в обе стороны, и обе вредны по-разному:

  • описано, но не работает. Разработчик читает документацию, шлёт запрос и получает 404. Доверие к остальной документации падает мгновенно — раз здесь соврали, где ещё?
  • работает, но не описано. Функциональность просто не существует для того, кто её ищет. Её не найдут, за неё не заплатят.

Второй случай коварнее, потому что никто не жалуется. Жаловаться некому: пользователь не знает, что эндпоинт есть.

У нас так и вышло. В API Lix.li работали три эндпоинта для конверсий — лента событий и приём постбеков, одиночный и пакетный, — и ни одного из них не было в спецификации. Код работал, тесты проходили, на витрине API-лендинга при этом были нарисованы два совсем других адреса, которых в коде не было вовсе. Обнаружилось это не по жалобе, а при сплошной сверке.

Как поймать расхождение автоматически

Лечится это не дисциплиной, а тестом. Идея простая: взять список путей из спецификации, взять список маршрутов из роутера и сравнить множества в обе стороны.

пути из openapi.yaml   →   есть такой маршрут?   →   иначе документация врёт
маршруты из роутера    →   есть такой путь?      →   иначе функциональность невидима

Проверку «описанное работает» удобно делать живым запросом: дёрнуть эндпоинт без авторизации и убедиться, что ответ — 401, а не 404. Отличие важное. 401 значит «маршрут найден, нужен ключ», 404 — «такого адреса нет». Никаких побочных эффектов при этом не происходит: аутентификация отрабатывает раньше, чем контроллер.

Обратную проверку делают по конфигурации маршрутов. Заодно придётся явно перечислить исключения — служебные эндпоинты, которым в публичном контракте не место. Список исключений полезен сам по себе: он заставляет один раз осознанно решить, что именно вы считаете публичным API.

Мы такой тест написали после истории с конверсиями. Проверка себя оправдала сразу: если убрать конверсии из спецификации, тест падает и называет оба пропавших адреса поимённо.

Две ловушки, о которых узнаёшь поздно

$ref и относительные пути. Если спецификация разбита на файлы, ссылки внутри неё разрешаются относительно адреса, с которого файл отдали. Корневой документ, доступный по /openapi, будет искать ./paths/links.yaml по адресу /paths/links.yaml — и не найдёт. В браузере через Swagger UI всё выглядит нормально, потому что там файл загружается со своего настоящего места, а вот генератор клиента на том же документе споткнётся. Либо отдавайте спецификацию только с её собственного каталога, либо собирайте её в один файл без внешних $ref.

Swagger UI пуст для поисковика. Страница документации на Swagger UI — это несколько строк разметки и скрипт, который дорисовывает содержимое в браузере. В исходном HTML нет ни одного адреса эндпоинта. Для читателя страница нормальная, для индексации — пустая. Добавьте сюда частую ошибку в robots.txt: правило Disallow: /api/ пишут, чтобы закрыть JSON-эндпоинты, а закрывают заодно и человеческую документацию по тому же префиксу. Закрывать стоит точный путь версии — например /api/1.0/, — а не весь раздел.

С чего начать

Если API уже есть, а описания нет, порядок примерно такой:

  1. Опишите один эндпоинт руками — этого хватит, чтобы понять структуру.
  2. Возьмите версию, которую уверенно держат ваши инструменты; по умолчанию 3.1.
  3. Разнесите файл по $ref, когда он перестанет помещаться на экран, и сразу зафиксируйте, с какого адреса он отдаётся.
  4. Поставьте тест, сверяющий спецификацию с роутером в обе стороны. До этого шага любые обещания об актуальности документации — обещания.

Посмотреть, как это выглядит на живом API, можно в документации API Lix.li: там описаны ссылки, группы, A/B-тесты и конверсии. Если интересно, как устроен сам сервис вокруг этого API, есть страница API сокращения ссылок, а из соседних тем — что такое вебхуки, разбор редиректов 301, 302, 307 и 308 и пример работы через PHP SDK.