Сайт использует сookies для хранения данных. Продолжая использовать сайт, вы даёте согласие на работу с этими файлами.

ОК
💻
Технологии
Опубликовано:
14.09.2026
Обновлено:
14.09.2026

RTK Query и OpenAPI: генерация типобезопасного API-клиента без рутины

Тимофей Ищенко

Коротко: Пошаговое руководство по генерации типобезопасного API-клиента и React-хуков в RTK Query из OpenAPI/Swagger с помощью @rtk-query/codegen-openapi.

Ручной перенос DTO-интерфейсов и декларация эндпоинтов в RTK Query неизбежно приводят к рассинхронизации контрактов между клиентом и сервером. Любое изменение поля на бэкенде, не замеченное во фронтенд-коде, превращается в скрытый баг в рантайме. Автоматическая генерация API-слоя на базе спецификации OpenAPI (Swagger) решает эту проблему: типы данных, методы запросов и React-хуки формируются напрямую из схемы.

Официальная утилита @rtk-query/codegen-openapi, входящая в экосистему Redux Toolkit, позволяет полностью исключить рутинное написание бойлерплейта и гарантирует строгую типобезопасность на этапе компиляции TypeScript.


Архитектура кодогенерации в RTK Query

Инструмент @rtk-query/codegen-openapi анализирует JSON- или YAML-схему OpenAPI (v2 или v3) и преобразует ее в полноценный API-слайс для RTK Query.

Концепция Base API и Code Splitting

Архитектура строится на механизме разделения эндпоинтов (injectEndpoints). Вместо создания монолитного клиента с нуля рабочий процесс разделяется на две части:

  1. Базовый API (emptySplitApi) — вручную созданный модуль, в котором настраиваются базовый URL, заголовки авторизации, обработка повторных попыток (retry) и кастомный baseQuery.
  2. Сгенерированный API — автоматически создаваемый файл, который импортирует базовый API и внедряет в него эндпоинты через вызов .injectEndpoints().

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

┌────────────────────────┐
│   OpenAPI / Swagger    │
│      Спецификация      │
└───────────┬────────────┘
            │  @rtk-query/codegen-openapi
            ▼
┌────────────────────────┐       ┌────────────────────────┐
│    Сгенерированный     │ ───▶  │      Базовый API       │
│  API-файл (Endpoints,  │ inject│    (BaseQuery, Auth,   │
│     Types, Hooks)      │       │     Custom Headers)    │
└────────────────────────┘       └────────────────────────┘

Что входит в сгенерированный артефакт

В выходном файле кодогенератор формирует:

  • TypeScript-типы: интерфейсы для тел запросов (Request Body), параметров строки запроса (Query Params), параметров пути (Path Params) и моделей ответов (Responses).
  • Эндпоинты RTK Query: вызовы builder.query и builder.mutation со строго привязанными типами аргументов и ответов.
  • React-хуки: готовые хуки вида useGetUsersQuery, useUpdateUserMutation и useLazyGetUsersQuery.

Пошаговая настройка @rtk-query/codegen-openapi

Шаг 1. Установка зависимостей

Для работы кодогенератора установите пакет в качестве dev-зависимости:

npm install -D @rtk-query/codegen-openapi

Убедитесь, что в проекте уже установлены базовые библиотеки @reduxjs/toolkit и react-redux.

Шаг 2. Создание базового API-слайса

Создайте файл src/shared/api/emptyApi.ts. В нем объявляется базовый инстанс с конфигурацией сети:

// src/shared/api/emptyApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const emptySplitApi = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: 'https://api.example.com/v1',
    prepareHeaders: (headers) => {
      const token = localStorage.getItem('access_token');
      if (token) {
        headers.set('authorization', `Bearer ${token}`);
      }
      return headers;
    },
  }),
  endpoints: () => ({}),
});

Шаг 3. Конфигурационный файл (openapi-config.ts)

В корне проекта или в директории конфигураций создайте файл настроек генератора. Пакет предоставляет интерфейс ConfigFile для проверки параметров:

// openapi-config.ts
import type { ConfigFile } from '@rtk-query/codegen-openapi';

const config: ConfigFile = {
  schemaFile: 'https://api.example.com/v1/openapi.json', // или локальный путь: './schema.json'
  apiFile: './src/shared/api/emptyApi.ts',
  apiImport: 'emptySplitApi',
  outputFile: './src/shared/api/generatedApi.ts',
  exportName: 'generatedApi',
  hooks: true,
};

export default config;

Основные параметры конфигурации:

  • schemaFile — URL или относительный путь к файлу схемы (JSON или YAML).
  • apiFile — путь к файлу с базовым API-клиентом.
  • apiImport — имя экспортируемой переменной базового API.
  • outputFile — путь к файлу, в который будет записан результат генерации.
  • exportName — имя экспортируемого расширенного API.
  • hooks — при значении true генерирует React-хуки для каждого эндпоинта.

Шаг 4. Настройка npm-скрипта и запуск генерации

Добавьте команду запуска в секцию scripts файла package.json:

{
  "scripts": {
    "codegen:api": "rtk-query-codegen-openapi openapi-config.ts"
  }
}

Запустите команду в терминале:

npm run codegen:api

После завершения процесса в файле src/shared/api/generatedApi.ts появятся типизированные методы и хуки, готовые к использованию в React-компонентах:

// Пример использования в компоненте
import { useGetUserByIdQuery, useUpdateUserMutation } from '@/shared/api/generatedApi';

export const UserProfile = ({ userId }: { userId: string }) => {
  const { data: user, isLoading, isError } = useGetUserByIdQuery({ id: userId });
  const [updateUser] = useUpdateUserMutation();

  if (isLoading) return <div>Загрузка...</div>;
  if (isError || !user) return <div>Ошибка загрузки</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <button onClick={() => updateUser({ id: userId, userDto: { name: 'Новое имя' } })}>
        Обновить
      </button>
    </div>
  );
};

Тонкая настройка: энумы, валидация и расширение эндпоинтов

Управление стилем перечислений (enumStyle)

По умолчанию генератор может компилировать OpenAPI enum в классические конструкции enum TypeScript. Если в проекте принят стандарт использования строковых литеральных типов или объектов as const, поведение генератора корректируется опцией enumStyle:

// openapi-config.ts
const config: ConfigFile = {
  // ...
  enumStyle: 'as-const', // варианты: 'enum' | 'as-const' | 'union'
};
  • 'union' формирует объединение типов: type Status = 'active' | 'inactive';
  • 'as-const' формирует объект-константу и выводит тип: export const Status = { Active: 'active', ... } as const;

Генерация регулярных выражений для валидации

Если в OpenAPI-схеме для строковых полей заданы свойства pattern, кодогенератор извлекает их в виде скомпилированных регулярных выражений:

export const userEmailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

Эти паттерны можно переиспользовать в схемах валидации форм (Zod, Yup) на стороне фронтенда, сохраняя полную синхронизацию с серверными правилами.

Инвалидация кэша через enhanceEndpoints

Сгенерированный файл нельзя редактировать вручную — любые изменения перезаписываются при повторном запуске генератора. Для настройки тегов кэширования (providesTags, invalidatesTags) используется метод enhanceEndpoints:

// src/shared/api/apiWithTags.ts
import { generatedApi } from './generatedApi';

export const enhancedApi = generatedApi.enhanceEndpoints({
  addTagTypes: ['User', 'Post'],
  endpoints: {
    getUserById: {
      providesTags: (result, error, arg) => [{ type: 'User', id: arg.id }],
    },
    updateUser: {
      invalidatesTags: (result, error, arg) => [{ type: 'User', id: arg.id }],
    },
  },
});

export const { useGetUserByIdQuery, useUpdateUserMutation } = enhancedApi;

Интеграция в рабочий процесс и CI/CD

Автоматизация генерации API-клиента требует выстроенных процессов в команде:

  1. Коммит сгенерированных файлов в репозиторий: сгенерированные файлы рекомендуется фиксировать в Git. Это обеспечивает быстрый старт проекта без предварительной сборки, гарантирует работу автокомплита в IDE сразу после клонирования репозитория и позволяет отслеживать изменения контрактов на этапе Code Review.
  2. Проверка актуальности в CI: в пайплайн сборки добавляется шаг, запускающий кодогенерацию и проверяющий статус рабочей директории. Если схема на бэкенде изменилась, а разработчик не обновил клиент, команда git diff --exit-code завершит сборку с ошибкой:
# Пример шага в GitHub Actions
- name: Check API Types Drift
  run: |
    npm run codegen:api
    git diff --exit-code src/shared/api/generatedApi.ts

Частые ошибки и ограничения кодогенерации

  • Неконсистентный или отсутствующий operationId: кодогенератор опирается на поле operationId в схеме для формирования имен методов и хуков. Если бэкенд не указывает operationId, имена эндпоинтов строятся на основе HTTP-метода и пути (например, getApiV1UsersById), что усложняет чтение кода.
  • Мутации с ответом 204 No Content: если бэкенд возвращает пустой ответ, но в схеме не указан тип возвращаемого значения void или пустой Response Object, TypeScript может типизировать ответ как unknown.
  • Прямое редактирование выходного файла: любые ручные изменения типов или параметров в сгенерированном файле теряются при повторной генерации. Вся кастомизация должна производиться через enhanceEndpoints или внешние утилитарные типы TypeScript.

Часто задаваемые вопросы (FAQ)

Можно ли генерировать несколько API-клиентов для разных микросервисов?

Да. Для этого создается массив конфигураций или отдельные конфигурационные файлы для каждого сервиса. Все они могут внедрять эндпоинты в единый emptySplitApi или использовать изолированные базовые слайсы для разных доменов.

Что делать, если на бэкенде нет валидных operationId?

Генератор сформирует имена функций автоматически по структуре пути и HTTP-глаголу. Однако предпочтительнее договориться с бэкенд-командой о корректной генерации operationId через Swagger-декораторы контроллеров.

Поддерживаются ли схемы OpenAPI в формате YAML?

Да, параметр schemaFile принимает прямые ссылки и пути как к .json, так и к .yaml / .yml файлам.

Как переопределить тип ответа, если схема бэкенда неточна?

Используйте enhanceEndpoints и метод transformResponse для преобразования данных либо уточняйте типы с помощью TypeScript-утилит (Omit, Pick, объединения типов) на уровне компонентов.

Как передавать динамические заголовки?

Динамические параметры запросов настраиваются один раз в emptySplitApi внутри prepareHeaders или через кастомную обертку над baseQuery. Сгенерированные эндпоинты автоматически наследуют эту конфигурацию.


Заключение

Использование @rtk-query/codegen-openapi переводит интеграцию фронтенда с API на уровень строгого контрактного программирования. Кодогенерация избавляет команду от ручного описания DTO-типов, снижает объем технического долга и предотвращает рассинхронизацию интерфейсов при непрерывной поставке изменений. Описанный подход позволяет безопасно масштабировать кодовую базу и сосредоточиться на реализации бизнес-логики приложения.

Источники

Это авторская статья, основанная на личном опыте и субъективном взгляде автора. Заметили ошибку или битую ссылку? Сообщите нам: info@codesrc.ru - мы оперативно исправим. Спасибо, что помогаете делать блог лучше.
Следите за нами в соцсетях:

Читайте также