Learning Platform
Глоссарий Troubleshooting
Урок 08.03 · 20 мин
Начальный
OpenAPISwagger UIRedocCodegenopenapi-python-client

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-документацию. Три самых популярных.

Сравнение средств просмотра OpenAPI
Swagger UIСамый старый и самый известный. JS-приложение, рендерит спеку в интерактивную страницу с 'Try it out'. Поставляется с FastAPI по умолчанию
RedocАльтернативный рендерер от Redocly. Трёхколоночный layout: навигация / описание / примеры. Лучше выглядит, но без 'Try it out' в open-source версии
StoplightStoplight Studio -- локальный GUI редактор спеки. Stoplight Elements -- embed-компонент. Хорош для команд, которые пишут спеку руками
Try it outКнопка 'отправить запрос прямо из браузера'. Ввели параметры, нажали -- увидели ответ сервера. Бесценно для exploration
Поиск по операциямФильтрация endpoints. Особенно полезно в больших API на 200+ операций
Code samplesГотовые примеры curl/Python/JS для каждого endpoint. Redoc хорош в этом: показывает примеры на 6 языках

Практически: для просмотра чужой спеки достаточно открыть editor.swagger.io, вставить YAML/JSON в левую панель — справа появится Swagger UI. Для документации своего API в FastAPI — /docs (Swagger UI) и /redoc (Redoc) уже работают «из коробки».

TIP

В команде типичный паттерн: для внутренних разработчиков — 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:

Workflow интеграции через openapi-python-client
1. Получить specURL вида /openapi.json или /v3/api-docs у Spring Boot, или файл от партнёра. Сохранить в репо локально для воспроизводимости
2. Validateopenapi-spec-validator или Spectral -- убедиться, что спека валидна. 80% спек 'из дикой природы' содержат мелкие нарушения, которые ломают codegen
3. Generateopenapi-python-client generate --path local.yaml. Получаем Python-пакет в текущей директории
4. Installpip install -e ./my-api-client/ -- установить как editable пакет в свой venv. Можно положить и в monorepo
5. Import & callfrom my_api_client.api.users import list_users -- стандартный import. Вызывайте методы
6. Update on spec changeПерегенерировать раз в неделю или при notification от backend-команды. CI-job 'spec changed -> regenerate -> run tests' закрывает риск

Шаг 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-стэком.

openapi-python-client vs openapi-generator
openapi-python-clientТолько Python. Современный код: dataclasses, async-первый подход через httpx, type hints. Pip install, no JVM
openapi-generator50+ языков. Универсальный, но генерируемый код 'усреднённый' -- не использует свежие фичи каждого языка по максимуму. Требует JVM или Docker
Когда выбрать первыйPython-only команда, важно качество генерируемого кода, async-first архитектура
Когда выбрать второйPolyglot стэк, или язык, который openapi-python-client не поддерживает (Go, Rust, Ruby, и т.д.)

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), но он минималистичнее.


Прагматика: когда не использовать кодогенерацию

Кодогенерация не серебряная пуля. Кейсы, когда лучше писать руками:

Когда отказаться от codegen
Спеки нет / она криваяБольшой процент API в дикой природе либо не имеет спеки, либо спека написана халтурно (нет схем, неправильные типы). Codegen на такой спеке хуже, чем ручной код
Один-два endpointВы интегрируетесь с API ради двух конкретных вызовов. Поставить целый сгенерированный пакет ради двух методов -- overkill
Эксперимент / прототипОдин раз дёрнули, посмотрели, выкинули. Кодогенерация -- для production-интеграций, не для исследовательских скриптов
Спека быстро меняетсяЕсли backend меняет API ежедневно (ранний этап стартапа), regenerate-цикл становится головной болью. Иногда проще держать тонкую обёртку из 50 строк requests-кода

В остальных случаях — для стабильных 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 главные две вещи:

  1. Просмотр — Swagger UI (через FastAPI /docs или editor.swagger.io) для exploration. Redoc для красивых публичных доков.
  2. Кодогенерация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 не доедет.


Проверка знанийKnowledge check
Junior сгенерировал клиент через openapi-python-client месяц назад. Сегодня код упал с UnexpectedStatus(400) на вызове create_order -- сервер требует обязательное поле shipping_method, которого в сгенерированной модели нет. Как этой ситуации избежать в будущем?
ОтветAnswer
Это классическая проблема spec drift: серверная спека ушла вперёд, локальный сгенерированный клиент остался в прошлом. Решения по приоритету: (1) Регулярный regenerate в CI -- добавить job, который раз в день качает /openapi.json с сервера, перегенерирует клиент, прогоняет тесты; если ломается -- open PR с обновлением. (2) Версионирование спеки в репозитории -- хранить openapi.yaml в репо, чтобы видеть diff в git и понимать, что именно поменялось. (3) Contract-тесты -- Schemathesis/Dredd регулярно проверяют, что сервер реально отвечает по спеке (защита от 'спека одна, реальность другая'). (4) Подписка на change notifications от backend-команды -- Slack-канал #api-changes или versioned releases. Главный вывод: сгенерированный клиент -- не 'забыл и забыл', а часть инфраструктуры, требующая регулярного обновления. Без этого вы получаете тот же drift, что и при ручном коде, плюс ложное чувство 'у меня же сгенерировано'.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Junior интегрируется с Python-only командой и стабильным API партнёра. Какой кодогенератор выбрать для генерации Python-клиента?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 3