javascript

OpenAPI во фронтенде или магия автоматизации

  • вторник, 22 июля 2025 г. в 00:00:07
https://habr.com/ru/companies/axenix/articles/926766/

Привет! Меня зовут Ларионова Екатерина, я фронтенд-разработчик в компании AXENIX.

В современной разработке программного обеспечения согласованность между документацией, дизайном API и его реализацией играет ключевую роль. Эффективно решить эту задачу помогает подход API-First, при котором проектирование интерфейсов становится отправной точкой всего процесса. Одним из основных инструментов, поддерживающих этот подход, является спецификация OpenAPI — мощный инструмент для описания RESTful API, который позволяет не только формализовать поведение сервисов, но и автоматизировать множество сопутствующих задач.

Мы все чаще сталкиваемся с парадоксом: с одной стороны, растут требования к скорости вывода продукта на рынок, с другой — увеличивается сложность приложений. При этом, разработчики тратят большое количество времени на рутинные задачи, такие как интеграция с API и написание boilerplate-кода. Именно здесь на помощь приходит автоматизация рутинных задач во фронтенде на основе OpenAPI-спецификаций и она может очень сильно упростить нам, фронтендерам, жизнь!

OpenAPI как рабочий инструмент

OpenAPI выходит за рамки простого описания API, становясь ключевым элементом в процессе разработки. Вот основные преимущества в использовании OpenAPI-спецификации:

  1. Автогенерация API-клиента — исключает рутинное написание повторяющегося кода за счёт автоматической генерации клиентов, типов и моков.

  2. Мок-серверы для разработки — позволяют фронтенду начать работу до готовности бэкенда, экономя время и упрощая параллельную разработку.

  3. Типизация и исключение ошибок — автоматическая генерация типов для TypeScript предотвращает опечатки и несоответствия данных.

  4. Упрощённое тестирование — генерация моков и стабов для тестов ускоряет проверку логики приложения.

  5. Надёжность и согласованность. Документация = источник истины — все компоненты (клиент, сервер, тесты) синхронизированы с описанием API, что снижает риск расхождений. Чёткий API-контракт минимизирует ошибки взаимодействия между командами.

Генерация клиентского кода из OpenAPI-спецификации

Автоматическая генерация клиентского кода из OpenAPI-спецификации — мощный инструмент в арсенале современного разработчика. Давайте разберёмся, как это работает на практике и как можно интегрировать этот процесс в ваш проект.

Подготовка спецификации

Перед началом работы нам понадобится сама OpenAPI-спецификация, которую обычно предоставляет аналитик. Фронтенд-разработчики могут затем работать с ней следующими способами:

  1. Локальный файл

  2. Swagger UI — через URL документации вашего API

  3. Git-репозиторий — например, из корпоративного GitLab

Лучше всего хранить OpenAPI-спецификацию в Git-репозитории — это обеспечивает версионность, контроль изменений и доступ для всей команды. Вот ряд плюсов:

  1. Сохранение истории правок — если API меняется, фронтендеры могут сравнить старую и новую версии спецификации через git diff.

  2. Синхронизация — все разработчики работают с актуальной версией, а не с локальными копиями.

  3. CI/CD интеграция — можно генерировать из спецификации код при каждом обновлении репозитория.

В данной статье мы рассмотрим самый простой способ работы с OpenAPI-спецификацией — использование локального файла. Этот вариант идеально подходит для быстрого старта т.к. не требует настройки Git или доступа к серверу документации.

Поместите файл спецификации schema.yaml в ваш проект. В рамках данной статьи для примера будет использоваться папка /openapi-schemas в корне проекта.

ваш_проект/
├── openapi-schemas/   # Папка из примеров
│   └── schema.yaml    # Основной файл спецификации
├── src/               # Исходный код проекта
└── ...                # Остальные файлы

Теперь вы можете ссылаться на этот файл в инструментах разработки.

Настройка инструментов генерации

Для генерации кода мы будем использовать OpenAPI Generator — один из самых популярных инструментов в этой области. Он позволяет автоматически создавать клиентские и серверные SDK, документацию и другую логику, связанную с API, на основе OpenAPI-спецификации.

Перед использованием OpenAPI Generator важно знать главное техническое требование: у вас должна быть установлена Java. Скачать ее можно с официального сайта Oracle.

Установите CLI-версию генератора как dev-зависимость в ваш проект с помощью команды:

npm install @openapitools/openapi-generator-cli --save-dev

Конфигурация генератора

Создайте в корне проекта файл openapitools.json с базовой конфигурацией:

{
  "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
  "spaces": 2,
  "generator-cli": {
    "version": "7.13.0",
    "generators": {
      "api-axfood": {
        "generatorName": "typescript-axios",
        "output": "./src/shared/api/api-axfood",
        "inputSpec": "./openapi-schemas/schema.yaml"
      }
    }
  }
}

При необходимости добавьте дополнительные настройки для кастомизации в поле additionalProperties:

{
  "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
  "spaces": 2,
  "generator-cli": {
    "version": "7.13.0",
    "generators": {
      "api-axfood": {
        "generatorName": "typescript-axios",
        "output": "./src/shared/api/api-axfood",
        "inputSpec": "./openapi-schemas/schema.yaml",
        "additionalProperties": {
          "apiPackage": "api",
          "modelPackage": "models",
          "withSeparateModelsAndApi": true,
          "withInterfaces": true,
          "supportsES6": true
        }
      }
    }
  }
}

Ключевые параметры:
generatorName: используемый генератор (typescript-axios, typescript-angular, typescript-fetch и др.)
output: путь для сгенерированных файлов
inputSpec: путь к OpenAPI-спецификации
additionalProperties: дополнительные настройки генерации

Популярные дополнительные свойства:
withSeparateModelsAndApi: флаг разделения API и моделей по папкам. По дефолту модели и API генерируются в один большой файл
apiPackage/modelPackage: название папок для файлов API и моделей
withInterfaces: генерация интерфейсов
supportsES6: использование ES6-синтаксиса

С полным списком параметров вы можете ознакомиться в официальной документации для генератора typescript-axios.

Интеграция в процесс разработки

Для удобства добавьте команду в раздел scripts файла package.json:

"scripts": {
  "api:generate": "openapi-generator-cli generate"
}

Теперь генерация кода выполняется одной командой: npm run api:generate

Проверка результатов

После выполнения команды откройте папку ./src/shared/api/api-axfood. Убедитесь, что:
• Контроллеры находятся в папке /api
• Модели — в папке /models

Создание API-клиента

Для работы со сгенерированным API создайте клиентский файл. Например, ./src/shared/api/client.ts:

import { Configuration, DefaultApi as AxFoodApi } from './api-axfood';

const apiConfig = new Configuration({
    basePath: '',
});

export const apiAxFood = new AxFoodApi(apiConfig);

Далее объект apiAxFood можно будет использовать в вашем проекте для доступа к методам API.


Генерация моков для API

Я достаточно часто сталкивалась с ситуацией, когда API ещё не было готово, но фронтенд уже нужно было разрабатывать. В таких случаях на помощь приходят моки — имитации реальных API-ответов. Далее мы рассмотрим, как автоматизировать процесс создания моков на основе OpenAPI-спецификации

Для генерации ответов будем использовать библиотеку msw-auto-mock, которая является расширением библиотеки MSW (Mock Service Worker).

Установите необходимые библиотеки, выполнив следующую команду:
npm install --dev msw msw-auto-mock @faker-js/faker

После установки инициализируйте MSW в нашем проекте:
msw init public/ --save

Эта команда создаст необходимые файлы для работы Service Worker в указанной директории public/.

Генерация моков

Основная магия происходит при выполнении следующей команды. С помощью нее будут сгенерированы моки по нашей спецификации:
msw-auto-mock ./openapi-schemas/schema.yaml -o ./mock --typescript

Параметры:
./openapi-schemas/schema.yaml — путь к файлу спецификации
-o ./mock — директория для выходных файлов
--typescript — генерация TypeScript-кода

Интеграция моков в приложение

Для подключения сгенерированных моков достаточно добавить следующие строки кода в точку входа вашего приложения (обычно main.ts или index.ts):

import { worker } from "../mock/browser";

await worker.start();

Вот и все! Всего три команды и пара строк кода — и моки, сгенерированные по OpenAPI-спецификации, готовы к использованию.


Генерация UI-прототипов по OpenAPI с помощью ИИ

В качестве эксперимента я решила проверить, насколько современные ИИ-инструменты способны генерировать рабочие интерфейсы на основе OpenAPI-спецификаций. Для быстрого прототипирования этот метод показал себя неожиданно полезным, однако требовал небольшие доработки и ручную адаптацию. Важно понимать, что это скорее любопытная альтернатива традиционному подходу, а не готовое решение для production.

Можно использовать любой ИИ-инструмент для разработчиков.

Ключевой момент — формулировка задачи. Пример рабочего промпта:

Сгенерируй компоненты для прототипа на React и Antd по спецификации openapi. 
Используй все параметры из спецификации. 
Воспользуйся сгенерированными с помощью openapi-generator-cli typescript-axios моделями и контроллерами. 
Сделай интерфейс адаптивным.

Что мы конкретно указали:
• Технологический стек (React + Ant Design)
• Источник данных (OpenAPI + сгенерированные модели)
• Требования к адаптивности

И вот такой компонент у нас был сгенерирован.

import axios from 'axios';
import React, {useState} from 'react';
import {Form, Input, Select, Button, message} from 'antd';
import type {CreateOrderRequest, ModelError} from "../shared/api/api-axfood";
import {apiAxFood} from "../shared/api/client.ts";

const OrderForm: React.FC = () => {
    const [form] = Form.useForm();
    const [loading, setLoading] = useState(false);
    const [messageApi, context] = message.useMessage();

    const onFinish = async (values: CreateOrderRequest) => {
        setLoading(true);
        try {
            const response = await apiAxFood.createOrder(values);
            messageApi.success(`Заказ успешно оформлен! Номер заказа: ${response.data.orderId}`);
            form.resetFields();
        } catch (error) {
            if (axios.isAxiosError<ModelError>(error)) {
                if (error.response && error.response.data) {
                    const { code, message: errorMessage } = error.response.data;
                    messageApi.error(`Ошибка (${code}): ${errorMessage}`);
                } else {
                    messageApi.error('Произошла ошибка при оформлении заказа.');
                }
            } else {
                messageApi.error('Неизвестная ошибка.');
            }
        } finally {
            setLoading(false);
        }
    };

    return (
        <Form
            form={form}
            name="order_form"
            layout="vertical"
            onFinish={onFinish}
            initialValues={{paymentMethod: 'картой'}}
        >
            {context}
            <Form.Item
                label="Идентификатор корзины с товарами"
                name="cartId"
                rules={[{required: true, message: 'Пожалуйста, введите идентификатор корзины!'}]}
            >
                <Input type="number"/>
            </Form.Item>
            <Form.Item
                label="Адрес доставки заказа"
                name="deliveryAddress"
                rules={[{required: true, message: 'Пожалуйста, введите адрес доставки!'}]}
            >
                <Input.TextArea rows={4}/>
            </Form.Item>
            <Form.Item
                label="Способ оплаты"
                name="paymentMethod"
                rules={[{required: true, message: 'Пожалуйста, выберите способ оплаты!'}]}
            >
                <Select>
                    <Select.Option value="картой">Картой</Select.Option>
                    <Select.Option value="наличными">Наличными</Select.Option>
                </Select>
            </Form.Item>
            <Form.Item>
                <Button type="primary" htmlType="submit" loading={loading}>
                    Оформить заказ
                </Button>
            </Form.Item>
        </Form>
    );
};

export default OrderForm;

Итог

Впервые попробовав такой подход на реальном проекте, я заметила, что использование OpenAPI-спецификации действительно упрощает разработку на начальных этапах. Из неоспоримых преимуществ стоит отметить экономию времени – пара часов на настройку генератора экономит десятки часов разработки за счёт автоматической генерации типов для API и моков на основе спецификации. При этом значительно сокращается количество ошибок из-за строгой типизации. Кроме того, такой подход улучшает командное взаимодействие и уменьшает количество ошибок при интеграции, поскольку фронтенд- и бэкенд-разработчики начинают работать с единой спецификацией.

Однако, существуют и минусы. Например, плохо составленная OpenAPI-схема может привести к некорректно сгенерированным типам или неполным мокам. Также можно столкнуться с ограничениями самого генератора, особенно при работе со сложными сценариями или нестандартными форматами данных. В этом случае требуется ручная доработка и написание собственных шаблонов.

Все методы, описанные в статье, я разберу на бесплатном воркшопе «Работа с OpenAPI: от спецификации до готового решения». Присоединяйтесь, чтобы увидеть реальные примеры, задать вопросы и глубже погрузиться в тему. Подробности и регистрация по ссылке.

Как и любой инструмент, метод требует настройки под ваш проект. Но при грамотном внедрении он становится мощным конкурентным преимуществом для вашей команды. Стоит попробовать!