5 июля 2025 г.

Генерация клиента по OpenAPI спецификации: удобный способ связи между Frontend и Backend

Как упростить разработку и повысить надежность взаимодействия между Frontend и Backend с помощью OpenAPI и кодогенерации

Удобство разработки - одна из самых важных вещей для программиста, особенно indie-разработчика, так как это напрямую влияет на мотивацию писать что-то новое. Если разрабатывать сложно и неудобно - мотивации что-то писать становится меньше.

Два пути fullstack-разработки

При разработке полноценного сервиса зачастую требуется создать и backend, и frontend. У меня, как у Python-программиста два пути:

  1. Писать и Backend, и Frontend на Python. В таком случае Frontend создается при помощи шаблонов (например Jinja2).
  2. Писать Backend на Python, а Frontend на одном из JavaScript-фреймворков.

Сегодня я бы хотел затронуть второй вариант: отдельная разработка Backend на Python и Frontend на JavaScript.

Проблема на стыке Backend и Frontend

У нас уже есть все необходимые инструменты, чтобы удобно писать Backend на Python и Frontend на TypeScript - авто-дополнение кода работает прекрасно, типы переменных проверяются и т.д.

На стыке же этих систем не все так хорошо. В ходе активной разработки сервиса, backend может часто меняться, в том числе могут ломаться контракты API.

В соответствии с этим, я бы хотел, чтобы:

  1. Обращение к методам API со стороны Frontend были максимально похожи на простой вызов функции.
  2. Должна быть возможность проверить линтером, что Frontend вызывает корректные методы API с корректными составом и типом параметров.
  3. При обновлении контрактов API со стороны Backend-сервиса, разработчик НЕ должен руками проходиться по всему Frontend-проекту и искать где и что сломалось, это должно происходить автоматически.

К счастью, решение есть - мы можем генерировать клиент к нашему Backend-сервису по OpenAPI спецификации.

В идеале, наш Backend также должен уметь сам генерировать OpenAPI спецификацию на основе сигнатур методов API. Этому условию удовлетворяют, например, Python-фреймворки FastAPI и Litestar

Кодогенерация по OpenAPI на примере Litestar

Создадим максимально простой проект со следующей структурой:

.
├── backend
│   └── app.py
├── frontend
│   └── package.json

Рассмотрим пример с Litestar. Для начала инициализируем виртуальное окружение и установим litestar в качестве зависимости:

> cd ./backend
> python3 -m venv ./venv
> source ./venv/bin/activate
> pip install litestar

Далее напишем простейший сервер:

from litestar import Litestar, post


@post("/items/")
async def create_item(
    name: str,
    description: str,
) -> bool:
    return True

app = Litestar(route_handlers=[create_item])

Сервер создает некоторый объект на основе его имени и описании. Как же нам получить OpenAPI спецификацию этого сервиса? Очень просто! Достаточно воспользоваться командой:

> litestar --app app:app schema openapi

Команда создаст файл openapi_schema.json - это как раз и есть описание нашего сервиса в формате OpenAPI.

Теперь воспользуемся инструментом OpenAPI TypeScript чтобы сгенерировать TypeScript типы для обращения к нашему серверу:

> npx openapi-typescript openapi_schema.json -o ../frontend/api_gen.d.ts

Для обращения к API теперь требуется создать клиент на основе сгенерированных типов. С этим нам поможет библиотека OpenAPI Fetch:

> cd ../frontend
> npm i openapi-fetch

Наконец, можем создать наш API-клиент:


import type { paths } from './api_gen';
import createClient from 'openapi-fetch';

export const apiClient = createClient<paths>();

Проверим, что клиент генерируется верно и корректно подсказывает нам контракт API:

Автодополнение URL

Также, очень удобно, что клиент сам знает какие query-параметры принимает наш метод /items:

Автодополнение query-параметров

Итог

Таким образом, мы можем максимально просто связывать наши frontend и backend сервисы, а также быть уверенными, что frontend вызывает методы API с правильными параметрами. Всего хорошего!