OpenAPI 3.1: машинно-читаемый контракт API
В этом уроке мы рассмотрим, что такое OpenAPI Specification: как она появилась из Swagger, чем отличаются версии 2.0/3.0/3.1/3.2, какие у спеки обязательные секции и почему весь современный API-tooling — Swagger UI, Postman, кодогенераторы — это надстройка над одним YAML- или JSON-файлом.
Если REST — это набор соглашений о том, как использовать HTTP, то OpenAPI — это формальный язык для записи конкретных API: какие endpoints есть, какие параметры принимают, какие схемы JSON возвращают. Прочитав корректный OpenAPI-документ, вы узнаёте про API всё, что нужно для написания клиента, без единого вопроса в Slack автору.
Откуда взялся OpenAPI: краткая хронология
В 2010 году Tony Tam в компании Wordnik разработал внутренний формат для описания их HTTP API. Формат назывался Swagger. В 2011 он стал open-source, в 2014-2015 вокруг него вырос огромный экосистем инструментов: Swagger UI (рендерит спеку в интерактивный браузер), Swagger Codegen (генерирует клиенты на 30+ языках), Swagger Editor.
В 2015 году SmartBear (новый владелец Swagger) передал спецификацию в Linux Foundation, где была создана OpenAPI Initiative. Swagger 2.0 был переименован в OpenAPI Specification (OAS). Бренд Swagger остался за инструментами (Swagger UI, Swagger Editor), а сама спецификация теперь называется OpenAPI.
Практически: если вы пишете новый API в 2026 году — берите OpenAPI 3.1. Если интегрируетесь с готовым API — может прилететь любая версия от 2.0 до 3.2. Большинство инструментов (Swagger UI, Redoc, openapi-generator) поддерживает все эти версии, но конкретные фичи (например, webhooks как top-level объект) появились только в 3.1.
Различайте бренды. Swagger — это инструменты (Swagger UI, Swagger Editor, Swagger Codegen), которые делает SmartBear. OpenAPI — это спецификация, которой управляет OpenAPI Initiative. Когда коллега говорит «отправь мне Swagger», в 99% случаев он имеет в виду «отправь OpenAPI YAML/JSON, который можно открыть в Swagger UI».
Зачем нужна формальная спецификация
Без OpenAPI документация API живёт в Confluence, README или Notion. Это значит, что человек её читает, человек интерпретирует, человек пишет клиент по этой интерпретации. Опечатка в документации — клиент сломан. Документация устарела на квартал — клиент сломан. Backend поменял формат поля — frontend узнаёт об этом из 500 на проде.
OpenAPI решает эту проблему через простую идею: спецификация в машинно-читаемом формате, и из неё генерируются и документация для людей, и код для машин.
Это ровно та причина, по которой Junior DE стоит научиться читать OpenAPI как родной русский. Когда вам приносят задачу «забери данные из API сервиса X», и сервис X отдаёт https://api.example.com/openapi.yaml — вы за пять минут видите все endpoints, форматы и нужные заголовки. Без OpenAPI вы час разбираетесь по примерам в README и пишете в чат «а как у вас называется поле created_at или createdAt?».
Структура спецификации: семь ключевых секций
OpenAPI-документ — это один JSON или YAML файл с фиксированной верхнеуровневой структурой. У документа есть семь ключевых секций. Три обязательны, остальные опциональны, но в реальном API почти всегда присутствуют.
Заметим, что components — это библиотека определений, на которые ссылаются из paths через механизм $ref. Это критично для DRY: если у нас 30 endpoints возвращают один и тот же объект User, мы не пишем его 30 раз — мы выносим в components/schemas/User и ссылаемся.
YAML или JSON: какой формат выбрать
OpenAPI поддерживает два формата сериализации, эквивалентных по выразительности: JSON и YAML. Файл openapi.json и openapi.yaml могут описывать абсолютно одинаковый API. Большинство инструментов читает оба.
На практике: если спеку генерирует фреймворк (FastAPI, drf-spectacular, NestJS) — почти всегда JSON; если её пишут руками — почти всегда YAML. Конвертация в обе стороны тривиальна, в Python через yaml и json модули в две строки.
Минимальный валидный OpenAPI-документ
Посмотрим на самую маленькую спеку, которая пройдёт валидацию OpenAPI 3.1. У нас один endpoint, одна операция, один ответ.
openapi: 3.1.0
info:
title: Hello API
version: 1.0.0
paths:
/hello:
get:
summary: Возвращает приветствие
responses:
'200':
description: Успешный ответ
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "Hello, world!"
Эта спека описывает API из одного endpoint GET /hello, который возвращает JSON с полем message. Уже из этих 16 строк инструменты:
- Сгенерируют интерактивную документацию в Swagger UI
- Создадут typed Python-клиент с методом
client.get_hello() - Поднимут mock-сервер, который на запрос
GET /helloвернёт{"message": "string"} - Запустят contract-тесты, которые проверят, что реальный сервер действительно возвращает поле
messageтипа string
Заметьте структурную иерархию: paths -> путь /hello -> метод get -> responses -> статус-код '200' -> content -> media type application/json -> schema. Это типичная глубина OpenAPI: четыре-пять уровней вложенности. Поэтому YAML с отступами читать удобнее, чем JSON со скобками.
Скопируйте спеку выше в editor.swagger.io. Слева — YAML, справа — Swagger UI. Поменяйте Hello, world! на свой текст и нажмите «Try it out» — увидите, как работает интерактивная документация. Это и есть UX, который OpenAPI даёт бесплатно.
Чуть более реалистичный пример: /users/{id}
Минимальный пример выше — это игрушка. Реальный API содержит параметры пути, query-параметры, ошибки. Покажем второй уровень сложности: endpoint, который читает пользователя по ID, использует переиспользуемую схему и описывает ошибки 404.
openapi: 3.1.0
info:
title: Users API
version: 2.0.0
description: |
Управление учётками пользователей.
Документация: https://docs.example.com/users
servers:
- url: https://api.example.com/v2
description: Production
- url: https://staging-api.example.com/v2
description: Staging
paths:
/users/{user_id}:
get:
summary: Получить пользователя по ID
operationId: getUserById
tags: [users]
parameters:
- name: user_id
in: path
required: true
schema:
type: integer
minimum: 1
responses:
'200':
description: Пользователь найден
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: Пользователь не найден
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
User:
type: object
required: [id, email, created_at]
properties:
id:
type: integer
example: 42
email:
type: string
format: email
created_at:
type: string
format: date-time
Error:
type: object
required: [code, message]
properties:
code:
type: string
example: not_found
message:
type: string
Что нового по сравнению с минимальным примером:
servers— два окружения. Сгенерированный клиент даёт переключение через параметр конструктора.parameters— параметр путиuser_id, который описан как integer с минимумом 1. Если кто-то вызовет/users/-5, валидатор поймает ошибку.operationId— уникальный идентификатор операции. Кодогенератор использует его для имени метода:client.get_user_by_id(user_id=42).$ref— ссылка на переиспользуемую схему. Если у нас 20 endpoints возвращаютUser, мы не дублируем определение.- Несколько responses — кроме
200описан404. Клиент знает, что 404 содержит структуру{code, message}.
Как это выглядит на стороне FastAPI
В реальном Python-проекте OpenAPI-спеку обычно не пишут руками — её генерирует фреймворк из аннотаций. FastAPI генерирует OpenAPI 3.1 из подсказок типов и Pydantic-моделей. Посмотрим, как тот же endpoint выглядит в коде.
from datetime import datetime
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
app = FastAPI(title="Users API", version="2.0.0")
class User(BaseModel):
id: int
email: EmailStr
created_at: datetime
class Error(BaseModel):
code: str
message: str
@app.get(
"/users/{user_id}",
response_model=User,
responses={404: {"model": Error}},
tags=["users"],
)
def get_user_by_id(user_id: int) -> User:
if user_id != 42:
raise HTTPException(status_code=404, detail="not found")
return User(id=42, email="[email protected]", created_at=datetime.now())
Запустить uvicorn main:app, открыть http://localhost:8000/openapi.json — получите готовую спеку, очень близкую к нашему YAML-примеру. По адресу /docs будет Swagger UI, по /redoc — Redoc. Это и есть стандартный workflow в современном Python: типы -> автоматическая спека -> генерируемая документация.
Для Junior DE это значит следующее: если интегрируетесь с FastAPI-сервисом, спека уже есть по адресу <base_url>/openapi.json. Не нужно просить разработчиков «отправить документацию» — она генерируется автоматически из кода и всегда актуальна.
Что нового в OpenAPI 3.2 (Sept 2025)
OpenAPI 3.2, вышедший в сентябре 2025, добавляет несколько важных вещей, релевантных для Data Engineers:
На практике для Junior DE в 2026: пишите новые спеки как 3.1, но имейте в виду, что часть API в индустрии будет постепенно переезжать на 3.2 в течение 2026-2027. Инструменты Swagger UI и Redoc уже поддерживают 3.2.
Kubernetes: декларативные манифесты и API-версионирование
Где OpenAPI не подходит
OpenAPI — спецификация для HTTP-based API с request/response семантикой. Она хорошо описывает REST, частично описывает RPC-подобные API. Плохо подходит для:
- GraphQL — у GraphQL свой формат описания схемы (SDL). OpenAPI нерелевантен.
- gRPC — описывается через
.protoфайлы. OpenAPI используется только если поверх gRPC сделан REST-gateway. - WebSocket / двунаправленные протоколы — OpenAPI описывает только запрос-ответ. Для bidirectional streaming используется AsyncAPI (отдельный, родственный стандарт).
- Real-time / событийные системы — Kafka topics, AMQP queues — это AsyncAPI, не OpenAPI.
Для Junior DE правило простое: если API общается через HTTP запросами и ответами — OpenAPI. Если что-то другое — другая спецификация (или вообще без спецификации, что тоже встречается).
Итоги урока
OpenAPI — это машинно-читаемое описание HTTP API в формате JSON или YAML. Эволюция: Swagger 2.0 (2014) -> OpenAPI 3.0 (2017) -> 3.1 (2021, JSON Schema 2020-12) -> 3.2 (2025, streaming/QUERY/иерархия тегов). Сейчас baseline для нового API — 3.1.
Структура документа: обязательные секции openapi, info, paths; опциональные servers, components, security, tags, webhooks. YAML — стандарт для ручных спек, JSON — для генерируемых фреймворком.
Из одной OpenAPI-спеки получается: интерактивная документация (Swagger UI/Redoc), кодогенерированный клиент, mock-сервер, contract-тесты, Postman-коллекция. Это даёт огромный leverage: 200 строк YAML заменяют 1000 строк ручной документации и плюс минус 500 строк boilerplate-кода клиента.
В следующем уроке разберём components/schemas глубоко — это сердце спецификации, описывающее формы данных, и именно там Data Engineer проводит большую часть времени при чтении чужих API.