Tooling вокруг OpenAPI: от просмотра до кодогенерации
OpenAPI без инструментов — это просто YAML-файл. Сила OpenAPI в том, что вокруг него выросла большая экосистема: средства просмотра, кодогенераторы, mock-серверы, contract-тестеры. В этом уроке мы пройдём по самому полезному для Junior DE: чем смотреть спеку и как из неё сгенерировать Python-клиент за две команды.
Главная мысль урока: если вы видите OpenAPI у API партнёра — почти никогда не пишите HTTP-запросы руками через requests.get. Сгенерируйте typed клиент, импортируйте, зовите методы. Это в два-три раза меньше кода, в десять раз меньше ошибок и почти бесплатно при изменениях спеки.
Просмотр спеки: Swagger UI vs Redoc vs Stoplight
YAML или JSON файл — это машинно-читаемый формат. Чтобы его прочитал человек, нужен рендерер: инструмент, превращающий спеку в HTML-документацию. Три самых популярных.
Практически: для просмотра чужой спеки достаточно открыть editor.swagger.io, вставить YAML/JSON в левую панель — справа появится Swagger UI. Для документации своего API в FastAPI — /docs (Swagger UI) и /redoc (Redoc) уже работают «из коробки».
В команде типичный паттерн: для внутренних разработчиков — Swagger UI с «Try it out» (быстро потыкать), для внешних публикаций (developer portal) — Redoc (выглядит профессиональнее). FastAPI отдаёт оба варианта, выбирайте по аудитории.
Type hints в Python: PEP 484 и базовый синтаксис
Кодогенерация: зачем
Допустим, вы интегрируетесь с API на 30 endpoints. Без кодогенерации вы пишете руками:
import requests
def get_user(user_id: int) -> dict:
response = requests.get(
f"https://api.example.com/v2/users/{user_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
)
response.raise_for_status()
return response.json()
def list_orders(user_id: int, status: str | None = None, limit: int = 20) -> list[dict]:
params = {"limit": limit}
if status:
params["status"] = status
response = requests.get(
f"https://api.example.com/v2/users/{user_id}/orders",
params=params,
headers={"Authorization": f"Bearer {TOKEN}"},
)
response.raise_for_status()
return response.json()
# ... ещё 28 функций
Что не так с этим кодом:
- Boilerplate — каждая функция дублирует Authorization, raise_for_status, базовый URL
- Без типизации — параметры и возврат —
dict, никакого автодополнения - Хрупкость к изменениям — добавили поле в API, поменяли URL, переименовали query-параметр — клиент молча сломан до runtime
- Без валидации — если сервер вдруг вернёт
id: "42"(строка вместо int) — узнаете это, когда упадётtotal + 1
Кодогенерация решает все четыре проблемы одной командой.
openapi-python-client: рекомендуемый Python-кодогенератор
В Python-экосистеме есть несколько кодогенераторов. Лучший для современного Python (3.10+) — openapi-python-client. Это активно поддерживаемый проект, генерирующий typed клиент с поддержкой dataclasses, async-методов, httpx под капотом.
# Установка
pip install openapi-python-client
# Генерация клиента из локального файла
openapi-python-client generate --path openapi.yaml
# Или прямо с URL
openapi-python-client generate --url https://api.example.com/openapi.json
Команда создаёт каталог с готовым Python-пакетом. Структура:
my-api-client/
pyproject.toml
README.md
my_api_client/
__init__.py
client.py # Класс Client с базовым URL и токеном
models/ # Сгенерированные dataclasses из components/schemas
__init__.py
user.py
order.py
address.py
api/ # Функции для каждого endpoint, сгруппированные по тегам
users/
get_user_by_id.py
list_users.py
orders/
list_orders.py
create_order.py
types.py # Утилиты UNSET, Response, etc.
errors.py # UnexpectedStatus exception
Использование:
from my_api_client import Client
from my_api_client.api.users import get_user_by_id
from my_api_client.api.orders import list_orders
client = Client(
base_url="https://api.example.com/v2",
headers={"Authorization": "Bearer YOUR_TOKEN"},
)
# Sync вызов
user = get_user_by_id.sync(client=client, user_id=42)
print(user.email, user.created_at)
# Async вызов (тот же endpoint, есть метод asyncio)
import asyncio
async def main():
orders = await list_orders.asyncio(client=client, user_id=42, status="paid")
for order in orders:
print(order.id, order.total)
asyncio.run(main())
Что мы получаем «бесплатно»:
- Автодополнение в IDE: набираете
user.— IDE подсказывает поля - Type checking: mypy ругается, если передали
user_id="42"(строку вместо int) - Async из коробки: каждый endpoint имеет
.sync(),.asyncio(),.sync_detailed(),.asyncio_detailed() - Парсинг ответов в правильные dataclasses
Что генерируется: пример dataclass
Возьмём кусок спеки из прошлого урока:
User:
type: object
required: [id, email]
properties:
id:
type: integer
email:
type: string
format: email
role:
type: string
enum: [user, moderator, admin]
default: user
openapi-python-client сгенерирует примерно такой код:
# models/user.py
from typing import Optional
from dataclasses import dataclass, field
from .user_role import UserRole
@dataclass
class User:
"""User entity"""
id: int
email: str
role: UserRole = UserRole.USER
def to_dict(self) -> dict:
return {
"id": self.id,
"email": self.email,
"role": self.role.value,
}
@classmethod
def from_dict(cls, src_dict: dict) -> "User":
d = src_dict.copy()
return cls(
id=d["id"],
email=d["email"],
role=UserRole(d.get("role", "user")),
)
# models/user_role.py
from enum import Enum
class UserRole(str, Enum):
USER = "user"
MODERATOR = "moderator"
ADMIN = "admin"
Дальше в endpoint-функции:
# api/users/get_user_by_id.py
from typing import Optional
from ...client import Client
from ...models.user import User
def sync(*, client: Client, user_id: int) -> Optional[User]:
response = client.get_httpx_client().get(
url=f"/users/{user_id}",
)
if response.status_code == 200:
return User.from_dict(response.json())
if response.status_code == 404:
return None
response.raise_for_status()
Это рабочий код, который вам не пришлось писать. И он будет переписан автоматически, когда обновится спека.
Workflow: получить spec -> generate -> import -> use
Стандартный процесс работы Junior DE с любым новым API:
Шаг 6 критичен. Проблема не в генерации, а в дисциплине обновления. Если ваш клиент завис на spec-версии за январь, а сервер уехал в апреле — клиент работает по старому контракту, и вы узнаете об этом, когда сервер вернёт 400 на «обязательное поле, появившееся после январской версии».
Альтернатива: openapi-generator (универсальный, на 50+ языков)
openapi-python-client хорош для Python, но узко специализирован. Если ваша команда работает на нескольких языках (Python для DE, Java для backend, TypeScript для frontend) — стоит знать про openapi-generator.
# openapi-generator -- Java-based, требует JVM. Установка через brew/sdkman/docker
brew install openapi-generator
# Генерация Python-клиента
openapi-generator generate \
-i openapi.yaml \
-g python \
-o ./my-client-py
# Тот же spec, TypeScript-клиент
openapi-generator generate \
-i openapi.yaml \
-g typescript-fetch \
-o ./my-client-ts
# Java
openapi-generator generate \
-i openapi.yaml \
-g java \
-o ./my-client-java
Поддерживается ~70 языков и frameworks. Меньше специализации под Python (генерируемый код использует urllib3, не httpx; модели — Pydantic v1 в старых версиях), но универсальность бесценна для команд с polyglot-стэком.
Stoplight Studio: дизайн спеки в GUI
openapi-python-client решает задачу «есть spec, нужен клиент». Обратная задача — «нужен spec, его пока нет» — решается либо в текстовом редакторе (для уверенных в YAML), либо в GUI-инструменте.
Stoplight Studio — десктопное приложение для дизайна OpenAPI. Вы работаете с формами, кликаете «добавить endpoint», заполняете поля — Studio генерирует YAML под капотом. Удобно для:
- Быстрого старта с нуля
- Менее технических людей в команде (product manager, который рисует API)
- Визуальной валидации схем (видно дерево схем и связи)
В Open Source альтернативе можно использовать swagger-editor (тот же editor.swagger.io локально через Docker), но он минималистичнее.
Прагматика: когда не использовать кодогенерацию
Кодогенерация не серебряная пуля. Кейсы, когда лучше писать руками:
В остальных случаях — для стабильных production-интеграций с описанным API — кодогенерация почти всегда выигрывает.
Mock-сервер: бонус из спеки
Помимо кодогенерации, из OpenAPI получают mock-сервер — HTTP-сервер, отвечающий по спеке фейковыми данными. Полезно, когда backend ещё не готов, а frontend/DE-клиент уже надо разрабатывать.
Лучший инструмент — Prism от Stoplight:
# Установка
npm install -g @stoplight/prism-cli
# Запуск mock-сервера на порту 4010
prism mock openapi.yaml
# Теперь в другом терминале
curl http://localhost:4010/users/42
# {"id": 42, "email": "string", "created_at": "2026-05-15T10:00:00Z"}
Prism генерирует ответы на основе схем (поле type: string -> строка "string", type: integer -> 0 или из example). Можно прокручивать happy path и edge cases (Prism умеет возвращать любой описанный response, включая 4xx/5xx, по запросу).
Итоги урока
OpenAPI без инструментов — просто YAML-файл. С инструментами — это документация, мок, кодогенерация, валидация. Для Junior DE главные две вещи:
- Просмотр — Swagger UI (через FastAPI
/docsили editor.swagger.io) для exploration. Redoc для красивых публичных доков. - Кодогенерация —
openapi-python-clientдля Python (рекомендуется),openapi-generatorдля polyglot. Получаете typed клиент за две команды и не пишете requests.get вручную.
Workflow интеграции: spec -> validate -> generate -> install -> import -> use. Регулярно перегенерировать при изменениях.
Stoplight Studio — для дизайна спеки в GUI. Prism — для mock-сервера. Эти инструменты делают жизнь Junior DE ощутимо легче, особенно при интеграции с большими API (Stripe, GitHub, Slack, AWS, Google Cloud — все имеют OpenAPI-спецификации).
В следующем модуле перейдём к аутентификации — без неё codegen-клиент даже до 401 не доедет.