В данном исследовательском отчете представлен всесторонний анализ методологии интеграции инструмента кодогенерации Orval с библиотекой Redux Toolkit Query (RTK Query) для управления взаимодействием с серверным API в приложении на React, TypeScript и Vite. Основное внимание уделяется архитектуре микрофронтендов и применению паттерна feature-sliced design. Отчет охватывает конфигурацию Orval, выбор стратегии генерации кода, реализацию кастомных клиентов, настройку хранилища Redux, модульную структуру API-слайсов, обработку ошибок и тегов, а также практические рекомендации по внедрению и оптимизации. Все выводы и рекомендации основаны исключительно на предоставленных источниках.
Начальная настройка Orval является фундаментальным шагом, который определяет качество и функциональность сгенерированного API-клиента. Учитывая, что Orval напрямую не поддерживает RTK Query в качестве целевого клиента, основной задачей становится использование его мощных возможностей по кастомизации для создания совместимого с данной библиотекой кода [[1,3]]. Проект, использующий TypeScript, React и Vite, создает благоприятную среду для такого подхода, так как Orval полностью написан на TypeScript и обеспечивает отличную типизацию [[6,10]].
Основной конфигурационный файл Orval обычно называется orval.config.ts или orval.config.js. В нем необходимо указать два ключевых параметра: input и output [[42]]. Параметр input.target задает путь к вашей OpenAPI 3.0.3 спецификации, которая служит единственным источником правды для всего API-интерфейса [[42]]. Этот файл должен быть корректно разбит и валиден согласно спецификации OpenAPI, поскольку любые синтаксические или семантические ошибки приведут к сбою процесса генерации.
Параметр output отвечает за структуру выходного кода. Для проекта с микрофронтендами и feature-sliced design наиболее предпочтительной является стратегия разделения (split). Эта опция позволяет организовать сгенерированный код по нескольким файлам, что значительно улучшает читаемость и поддерживаемость. Например, можно отделить модели данных (schemas) от самих функций запросов (services), что соответствует принципам разделения ответственности [[3,38]]. Конфигурация может выглядеть следующим образом:
import { defineConfig } from 'orval';
export default defineConfig({
petstore: {
input: {
target: './src/api/openapi.yaml',
},
output: {
mode: 'split',
preset: 'rtk-query', // Используется как отправная точка
workspace: {
models: {
path: '../gen/models',
name: 'models.ts',
},
services: {
path: '../gen/services',
name: 'petstore.service.ts',
},
},
override: {
mutator: {
template: './orval-mutator.template.hbs',
path: './api/mutator/custom-instance.ts',
},
},
},
},
});В этом примере Orval будет генерировать файлы с моделями в директорию ../gen/models и сервисный файл с функциями запросов в ../gen/services/petstore.service.ts. Ключевой момент здесь — использование опции override.mutator. Она позволяет указать собственный шаблон Handlebars (template) и путь к файлу с кастомной функцией-мутатором (path) [[3,36]]. Это тот самый механизм, который дает возможность адаптировать сгенерированный код для работы с RTK Query. Файл custom-instance.ts будет содержать логику HTTP-клиента, которая заменит стандартную реализацию Orval, позволяя интегрироваться с Redux-стором, добавлять заголовки авторизации и т.д. [[4,22]].
Кроме того, Orval предоставляет множество других полезных опций для тонкой настройки. Например, можно использовать baseUrl для базового URL вашего API, mock: true для автоматической генерации моков с помощью MSW (что отлично подходит для TTD и тестирования без готового бэкенда), и shouldSplitQueryKey для разделения пути эндпоинта на сегменты в ключе запроса, что помогает избежать коллизий [[33,36]]. Также стоит отметить, что Orval активно развивается, последний коммит в репозитории был недавно, что говорит о высоком уровне поддержки и надежности инструмента [[8,10]]. Однако важно понимать, что Orval не является единственным решением; существуют и другие инструменты, такие как @openapi-codegen/typescript, rapini и openapi-zod-client, которые также могут выполнять задачу кодогенерации [[8,18]].
| Параметр конфигурации | Описание | Пример значения | Источник |
|---|---|---|---|
input.target |
Путь к файлу OpenAPI 3.0.3 спецификации. | './src/api/openapi.yaml' |
[[42]] |
output.mode |
Режим генерации. split разделяет код на несколько файлов. |
'split' |
[[3]] |
output.preset |
Предустановленная конфигурация, отправная точка для генерации. | 'rtk-query' (или другой, если нужен кастомный шаблон) |
[[1]] |
output.workspace.models.path |
Путь для сохранения сгенерированных моделей (типизированных интерфейсов). | '../gen/models' |
[[38]] |
output.workspace.services.name |
Имя файла для сгенерированных сервисных функций запросов. | 'petstore.service.ts' |
[[42]] |
output.override.mutator.path |
Путь к пользовательской функции-мутатору. | './api/mutator/custom-instance.ts' |
[[3]] |
output.override.mutator.template |
Путь к кастомному шаблону Handlebars для генерации кода. | './orval-mutator.template.hbs' |
[[3]] |
mock |
Генерировать ли моки для использования с MSW. | true |
[[1]] |
Таким образом, правильная конфигурация Orval — это не просто запуск генератора, а проектирование системы, которая будет производить необходимый для дальнейшей работы код. Выбор режима split и использование кастомного мутатора являются ключевыми решениями, обеспечивающими соответствие сгенерированного кода современным практикам разработки на React и TypeScript, таким как микрофронтенды и feature-sliced design.
Центральным элементом интеграции Orval и RTK Query является создание кастомного мутатора. Поскольку Orval по умолчанию генерирует клиенты для библиотек типа react-query или axios, его нужно научить "говорить" на языке RTK Query [[1,3]]. Это достигается путем предоставления Orval пути к пользовательскому HTTP-клиенту через опцию override.mutator в конфигурационном файле [[3]]. Задача этого клиента — не просто выполнить HTTP-запрос, но и интегрироваться с жизненным циклом Redux-приложения, например, для получения токена аутентификации из глобального состояния перед отправкой запроса. Хотя в конкретном запросе указано, что обработка аутентификации не требуется, такой мутатор является обязательным компонентом для любой серьезной интеграции.
Структура кастомного мутатора представляет собой функцию, принимающую объект с информацией о запросе и возвращающую Promise<T>. Эта функция должна быть экспортирована из указанного в конфигурации файла, например, ./api/mutator/custom-instance.ts [[2]]. Внутри этой функции можно импортировать fetch и любые другие необходимые зависимости, включая useDispatch и useSelector из react-redux, чтобы иметь доступ к Redux-стору [[4]]. Пример такого мутатора может выглядеть следующим образом:
// ./api/mutator/custom-instance.ts
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { selectAuthToken } from '../features/auth/authSlice'; // Пример импорта токена из слайса аутентификации
export const customInstance = ({ url, method, params, body }) => {
return async () => {
const dispatch = useDispatch();
const authToken = useSelector(selectAuthToken); // Получаем токен из Redux store
const headers = {
'Content-Type': 'application/json',
...(authToken && { Authorization: `Bearer ${authToken}` }),
};
const response = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
};
};Этот мутатор оборачивает нативный fetch и добавляет логику для автоматического вставления Bearer-токена в заголовки запросов, если он существует в состоянии Redux. Это демонстрирует, как Orval можно адаптировать для выполнения сложных задач, выходящих за рамки простого HTTP-запроса. Важно отметить, что Orval также поддерживает использование httpClient: 'fetch' в режимах react-query, vue-query и других, что указывает на его внутреннюю готовность к работе с fetch, но требует явного указания кастомного мутатора для генерации кода, совместимого с createApi RTK Query [[3]].
Использование кастомного мутатора открывает широкие возможности для расширения функциональности. Например, можно реализовать логику повторных попыток запросов при возникновении сетевых ошибок, добавить таймауты для долгих запросов через httpResolverOptions (если используется @rtk-query/codegen-openapi), или даже выполнять несколько запросов внутри одного мутатора для реализации сложной бизнес-логики [[11,14]]. Такой подход позволяет превратить Orval из простого генератора типов в мощный инструмент для создания сложных и настраиваемых API-клиентов.
Однако стоит учитывать некоторые потенциальные проблемы. Во-первых, сложность мутатора напрямую влияет на читаемость и поддерживаемость кода. Слишком много логики в одном месте может сделать его трудным для тестирования. Во-вторых, при использовании кастомных мутаторов следует внимательно следить за их производительностью, особенно если они выполняют асинхронные операции, такие как чтение из Redux-стора. Несмотря на эти вызовы, создание кастомного мутатора остается наиболее гибким и мощным способом адаптации Orval для работы с RTK Query, позволяя точно контролировать каждый аспект взаимодействия с сервером.
Создание единого, централизованного API-слайса с помощью createApi является официальной рекомендованной практикой от команды Redux Toolkit [[31,32]]. Этот подход заключается в том, чтобы определить один основной API-слайс, который будет содержать все эндпоинты из OpenAPI-схемы. Он регистрируется в Redux-хранилище вместе со всеми другими редьюсерами и middleware, что обеспечивает единое место для управления состоянием API, эффективное кэширование и автоматическую повторную загрузку данных [[5,6]].
Для реализации этого подхода в контексте генерации кода с помощью Orval, первым шагом является создание пустого API-слайса. В одном из файлов (например, api/baseApi.ts) мы вызываем createApi без определения endpoints:
// api/baseApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const baseApi = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: [], // Типы тегов будут добавлены динамически
});Здесь мы задаем reducerPath — уникальный ключ для регистрации редьюсера в сторе, baseQuery — основную функцию для выполнения запросов (в нашем случае, fetchBaseQuery с базовым URL /api из OpenAPI-схемы), и пустой массив tagTypes, куда впоследствии будут добавлены типы тегов [[31,46]]. Важно всегда указывать reducerPath, так как пропуск этого параметра приведет к ошибкам времени выполнения, даже если TypeScript не выдает предупреждений [[46]].
Следующий этап — интеграция сгенерированного кода. Вместо того чтобы генерировать хуки напрямую, Orval будет генерировать функции, которые можно использовать для инъекции эндпоинтов в уже существующий API-слайс. Для этого используется метод injectEndpoints [[24,35]]. Мы можем создать отдельный файл (например, api/injectGeneratedApi.ts), который будет отвечать за этот процесс:
// api/injectGeneratedApi.ts
import { baseApi } from './baseApi';
// Импортируем сгенерированные эндпоинты (гипотетический импорт)
// import { generatedEndpoints } from '../gen/services/generatedApi';
// Инжектируем сгенерированные эндпоинты в наш основной API-слайс
baseApi.injectEndpoints({
isGlobal: true, // Эндпоинты будут добавлены глобально
endpoints: (build) => ({
// ...generatedEndpoints(build),
// Пример вручную определенного эндпоинта
getMe: build.query<User, void>({
query: () => '/auth/me',
providesTags: ['User'],
}),
}),
});
// Экспортируем сгенерированные хуки
export const {} = baseApi.endpoints;Этот подход идеально подходит для микрофронтенд-архитектуры. Каждый микрофронтенд может иметь свой собственный файл инъекции, где он будет добавлять только те эндпоинты, которые ему необходимы. Это позволяет достичь максимального дерева-стримминга (tree-shaking), так как в итоговый бандл будет включен только код, действительно используемый в приложении. Компоненты в рамках микрофронтенда могут импортировать свои хуки напрямую из этого файла, что делает зависимость от API четко очерченной и локализованной.
Однако, когда эндпоинты генерируются средствами Orval, возникает проблема совместимости с @rtk-query/codegen-openapi. Как было отмечено в обсуждении на GitHub, RTK Query не поддерживает разделение типов и эндпоинтов в разные файлы [[16]]. Это означает, что если Orval генерирует модели и эндпоинты в разных местах, то codegen-openapi не сможет их корректно связать. Поэтому, если выбран второй подход, потребуется либо использовать один выходной файл, либо применять post-process скрипты для объединения кода [[45]]. Для Orval же более естественным и гибким является подход с разделением файлов, который хорошо сочетается с injectEndpoints.
В итоге, выбор между этими двумя подходами зависит от приоритетов проекта. Единый API-слайс с injectEndpoints обеспечивает лучшую модульность, совместимость с микросервисами и контроль над размером бандла. Он требует некоторой ручной настройки для интеграции с Orval, но предлагает гибкость и масштабируемость. Подход с codegen-openapi проще в настройке, но менее гибок в плане организации кода и может привести к проблемам с разделением кода в больших проектах.
Эффективное управление состоянием API, включая обработку ошибок, кэширование и инвалидацию данных, является одной из главных сильных сторон RTK Query. Эти механизмы напрямую влияют на пользовательский опыт и производительность приложения. При интеграции с Orval важно понимать, как генерируемая из OpenAPI спецификации логика ошибок и какие дополнительные настройки могут потребоваться.
Обработка ошибок: RTK Query имеет встроенную систему обработки ошибок. Когда baseQuery (например, fetchBaseQuery) получает HTTP-ответ с кодом состояния вне диапазона 2xx, он преобразует его в объект ошибки, который затем становится доступным в поле error возвращаемого объекта хука [[15]]. Этот объект ошибки имеет тип FetchBaseQueryError | SerializedError [[15]]. FetchBaseQueryError содержит полезные поля, такие как status (HTTP-статус) и data (тело ответа сервера). Для безопасной проверки типа ошибки рекомендуется использовать type guard, например, проверку 'status' in error [[15]]. Если в OpenAPI-схеме определены структуры ошибок (как в примере с ErrorResponse), Orval сгенерирует для них TypeScript-интерфейсы, и эти типы будут использоваться для описания поля error.data. Таким образом, приложение может типобезопасно работать с деталями ошибки, например, для отображения пользователю сообщений о невалидных полях формы, как в примере ValidationErrorExample [[1]]. Любая дополнительная трансформация данных до их попадания в кэш должна выполняться внутри transformResponse или queryFn [[14,43]].
Кэширование и теги: RTK Query кэширует результаты запросов (query endpoints) по умолчанию в течение 60 секунд [[20,31]]. Ключом для кэша по умолчанию служит combinable строка из URL-пути и сериализованного объекта аргументов запроса. Однако для большей гибкости и контроля над данными используются теги (tags). Теги представляют собой пары (type, id), где type — это строка, описывающая сущность (например, 'Post'), а id — уникальный идентификатор сущности (например, 1). Endpoints, которые возвращают список сущностей, должны использовать опцию providesTags, чтобы сообщить RTK Query, какие именно данные были получены. Мутации (endpoints с mutation: true), которые изменяют или удаляют сущности, должны использовать опцию invalidatesTags, чтобы указать, какие кэшированные данные нужно удалить или пометить как устаревшие [[7,13]]. Например, после успешной мутации deletePost, которая инвалидирует теги { type: 'Post', id: '1' }, все запросы, предоставлявшие тег { type: 'Post', id: '1' }, будут автоматически повторно выполненными. Это позволяет поддерживать UI в актуальном состоянии без необходимости принудительного рефетча всех страниц. Важно отметить, что если несколько эндпоинтов инжектируются в один API-слайс, их общее количество рефетчей после мутации может превышать ожидаемое (например, три GET-запроса вместо одного), если они все инвалидируют одни и те же теги. Это связано с тем, как RTK Query отслеживает зависимости запросов [[28]].
Настройка поведения кэша: Поведение кэша можно настроить с помощью нескольких опций в baseQuery и в самих эндпоинтах. В fetchBaseQuery можно установить validateStatus для определения, какие HTTP-статусы считаются успешными. В createApi можно изменить время, в течение которого неиспользуемые данные остаются в кэше, с помощью опции keepUnusedDataFor (по умолчанию 60 секунд) [[20,39]]. В builder.query можно настроить поведение при повторном запросе с помощью refetchOnMountOrArgChange и refetchOnFocus [[13]]. Эти настройки позволяют тонко настраивать производительность и поведение приложения в зависимости от конкретных сценариев использования.
В контексте генерации кода, Orval не может автоматически определить, какие теги должны использоваться для каждого эндпоинта. Разработчику необходимо либо вручную определить эти опции в файле инъекции (injectGeneratedApi.ts), либо использовать плагины или кастомные шаблоны, чтобы расширить сгенерированный код. Например, можно написать post-process скрипт, который анализирует OpenAPI-схему (например, аннотации или теги) и автоматически генерирует providesTags и invalidatesTags для каждого эндпоинта.
Прямолинейная реализация интеграции Orval и RTK Query в рамках полнофункционального проекта требует последовательного выполнения нескольких шагов, начиная с настройки инструментов и заканчивая использованием сгенерированных хуков в компонентах. Этот процесс можно условно разделить на три фазы: подготовка окружения, генерация и интеграция кода, и использование API в приложении.
Фаза 1: Подготовка окружения
Первым шагом является установка необходимых зависимостей. Для проекта с React, TypeScript, Vite, Redux Toolkit и RTK Query потребуется установить Orval, его типы и кастомный мутатор.
npm install --save-dev @orval/core @orval/query orval
npm install --save react-redux zustand # Примеры для TS-ReduxПосле установки необходимо настроить orval.config.ts в соответствии с описанными выше принципами, определив входной файл OpenAPI, выходную структуру и, самое главное, путь к кастомному мутатору [[3,42]]. После завершения конфигурации можно запустить процесс генерации кода:
npx orvalЭта команда сгенерирует файлы с моделями и сервисными функциями в указанных директориях. На этом этапе важно убедиться, что сгенерированный код корректно работает. Можно создать простой тестовый файл, импортировать сгенерированные функции и попробовать выполнить один из запросов, чтобы убедиться в работоспособности самого HTTP-клиента.
Фаза 2: Генерация и интеграция кода
Как было подробно рассмотрено ранее, основной задачей на этом этапе является интеграция сгенерированного кода с RTK Query. Если выбран подход с injectEndpoints, создается основной API-слайс (baseApi.ts) и отдельный файл для инъекции (injectGeneratedApi.ts). В файле инъекции происходит импорт сгенерированных эндпоинтов (или их рукописное определение) и их добавление в baseApi с помощью baseApi.injectEndpoints() [[24,35]]. Важно правильно настроить reducerPath в createApi, чтобы избежать ошибок времени выполнения [[46]].
Если же используется @rtk-query/codegen-openapi, процесс немного иной. Необходимо создать конфигурационный файл для генератора (например, rtk-q-gen.config.ts), указав в нем путь к OpenAPI-схеме, путь к файлу с пустым API и желаемый выходной файл с эндпоинтами [[11]]. Запуск генератора (npx @rtk-query/codegen-openapi rtk-q-gen.config.ts) создаст новый файл, который должен быть импортирован и использован в основном API-слайсе.
В любом случае, после интеграции необходимо добавить редьюсер и middleware API-слайса в Redux-хранилище:
// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import { baseApi } from '../api/baseApi';
export const store = configureStore({
reducer: {
[baseApi.reducerPath]: baseApi.reducer,
// Другие редьюсеры
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(baseApi.middleware),
});
// Типы для useAppDispatch и useAppSelector
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;Фаза 3: Использование API в приложении
После успешной интеграции API-слайса в хранилище, его можно использовать в React-компонентах с помощью сгенерированных хуков. Эти хуки имеют удобные имена, например, useGetUsersQuery или useCreatePostMutation, и возвращают объект с информацией о состоянии запроса [[5,23]]. Пример использования в компоненте:
// UserList.tsx
import React from 'react';
import { useGetUsersQuery } from '../api/injectGeneratedApi';
const UserList: React.FC = () => {
const { data, error, isLoading } = useGetUsersQuery();
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error fetching users</p>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};В этом примере компонент использует хук useGetUsersQuery, полученный из интегрированного API. Он получает доступ к данным (data), ошибке (error) и состоянию загрузки (isLoading), и на основе этих значений отрисовывает UI. Весь код, связанный с управлением состоянием API, такой как кэширование, повторные запросы и обработка ошибок, полностью абстрагирован внутри RTK Query, что позволяет сосредоточиться на бизнес-логике компонента.
Важно отметить, что при переходе на новую версию API (и, соответственно, новую OpenAPI-схему), весь процесс повторяется: сначала обновляется спецификация, затем запускается генерация кода, и, возможно, потребуется внести небольшие ручные изменения в файлы интеграции, чтобы они соответствовали новой структуре API.
Выбор между различными методологиями интеграции Orval и RTK Query напрямую влияет на архитектуру, поддерживаемость и масштабируемость приложения. Анализ двух основных подходов — кастомный мутатор с injectEndpoints и автоматическая генерация с помощью @rtk-query/codegen-openapi — позволяет сделать осознанный выбор в зависимости от приоритетов проекта.
Подход 1: Кастомный мутатор с injectEndpoints (Рекомендуемый)
Этот подход является более гибким и модульным.
- Преимущества:
- Высокая модульность: Совместим с архитектурой микрофронтендов и feature-sliced design. Каждый модуль или микрофронтенд может иметь свой собственный файл инъекции, где он добавляет только необходимые ему эндпоинты. Это позволяет достичь максимального дерева-стримминга и минимизировать размер финального бандла [[24,35]].
- Гибкость: Позволяет генерировать код в произвольной структуре (например, разделение моделей и сервисов) благодаря опции
output.mode: 'split'в Orval [[3]]. Это улучшает читаемость и организацию кода. - Полнота контроля: Разработчик полностью контролирует процесс интеграции, включая добавление тегов кэширования, настройку
baseQueryи обработку специфических для приложения сценариев, таких как авторизация или трансформация данных.
- Недостатки:
- Больше ручной работы: Требуется написать код для инъекции эндпоинтов и, возможно, вручную определять опции
providesTagsиinvalidatesTags. - Потенциальные проблемы совместимости: Как уже упоминалось, Orval не может генерировать эндпоинты и типы в разных файлах для
codegen-openapi, что создает дилемму между удобством генерации Orval и требованиями генератора RTK Query [[16]].
- Больше ручной работы: Требуется написать код для инъекции эндпоинтов и, возможно, вручную определять опции
Подход 2: Автоматическая генерация с @rtk-query/codegen-openapi
Этот подход является более автоматизированным и быстрым в реализации.
- Преимущества:
- Простота и скорость: Минимальное количество шагов для настройки. Процесс сводится к созданию конфигурационного файла и запуску команды.
- Автоматическое определение тегов: Генератор может автоматически определять
providesTagsиinvalidatesTagsна основе аннотаций в OpenAPI-схеме, что экономит время.
- Недостатки:
- Меньшая гибкость: Жесткая привязка к структуре, которую генерирует
codegen-openapi. Возможности по разделению кода и модульности ограничены. - Проблемы с разделением кода: Как обсуждалось, это может привести к увеличению размера бандла, так как все сгенерированные эндпоинты будут включены в один файл, даже если они используются не во всех частях приложения.
- Меньшая гибкость: Жесткая привязка к структуре, которую генерирует
Альтернативные инструменты
Хотя Orval является мощным инструментом, стоит упомянуть и альтернативы. @openapi-codegen/typescript — еще один популярный инструмент, который может генерировать клиентские SDK из OpenAPI-схем. Однако, как и Orval, он требует ручной интеграции с RTK Query. Инструменты вроде rapini или openapi-zod-client предлагают генерацию схем и клиентов на основе Zod, что может быть полезно для runtime-валидации, но также не решает проблему интеграции с RTK Query "из коробки". Выбор инструмента часто сводится к предпочтениям команды и знакомству с конкретным пакетом.
Итоговые рекомендации:
Для проекта, который стремится к максимальной модульности, масштабируемости и соответствию современным архитектурным паттернам (микрофронтенды, feature-sliced design), настоятельно рекомендуется использовать подход с кастомным мутатором и injectEndpoints. Да, он требует больше начальных усилий по настройке, но в долгосрочной перспективе он предоставляет беспрецедентный уровень контроля и гибкости. Это позволит создать чистую, легко поддерживаемую и высокопроизводительную систему управления API.
Если же проект находится на ранней стадии разработки, или команда предпочитает минимальные затраты на настройку ради быстрого старта, и нет опасений по поводу будущего увеличения размера бандла, то можно рассмотреть подход с @rtk-query/codegen-openapi. Однако следует быть готовым к возможным ограничениям в будущем при расширении функциональности и усложнении архитектуры.