Platform
    • RTK Query
    • Mock Server (MSW)
    • Data Converters
    • OpenAPI Codegen
Projects
  • API Connections & Tools
  1. Documentation
  2. Documentation

Управление данными (RTK Query)

Детальное руководство по архитектуре RTK Query (Code Splitting).

Архитектура разделения кода RTK Query

Архитектурный инвариант: createApi вызывается один раз — в shared/api/backend/base.api.ts. Все сущности внедряют эндпоинты в единый baseApi (один кэш, один реестр тегов). Отдельный createApi допустим только для внешнего сервиса с другим baseUrl.

Файловая структура API-ядра

01Глобальный реестр тегов (Shared Layer)

Для корректной работы инвалидации providesTags / invalidatesTags, все возможные теги собираются в централизованный конфиг.

shared/api/backend/tags.config.ts
1export const ENUM_API_TAGS = { 2 USER: "User", 3 BOOKING_ORDERS: "Booking Orders", 4 // и другие теги... 5} as const;

02Action истечения сессии (Shared Layer)

Action объявлен в shared, поэтому baseApi не зависит от entities. Auth slice подписывается на него через extraReducers и сбрасывает сессию.

shared/api/backend/session-expired.action.ts
1import { createAction } from "@reduxjs/toolkit"; 2 3/** Диспатчится baseApi при 401; auth slice подписывается через extraReducers */ 4export const sessionExpired = createAction("api/sessionExpired"); 5 6// entities/auth/model/auth.slice.ts 7// extraReducers: (builder) => { 8// builder.addCase(sessionExpired, () => initialState); 9// }

03Базовый API инстанс с auth guard (Shared Layer)

Единственный createApi в проекте. Его base query оборачивает fetchBaseQuery: при 401 диспатчит sessionExpired() из shared, не импортируя entities.

shared/api/backend/base.api.ts
1import { 2 type BaseQueryFn, 3 type FetchArgs, 4 type FetchBaseQueryError, 5 createApi, 6 fetchBaseQuery 7} from "@reduxjs/toolkit/query/react"; 8import { ENV } from "@/shared/config"; 9import { sessionExpired } from "./session-expired.action"; 10import { ENUM_API_TAGS } from "./tags.config"; 11 12const rawBaseQuery = fetchBaseQuery({ 13 baseUrl: ENV.VITE_API_URL, 14 credentials: "include" 15}); 16 17/** 401 → sessionExpired(); слой shared не импортирует entities */ 18const baseQueryWithAuthGuard: BaseQueryFn< 19 string | FetchArgs, 20 unknown, 21 FetchBaseQueryError 22> = async (args, api, extraOptions) => { 23 const result = await rawBaseQuery(args, api, extraOptions); 24 25 if (result.error?.status === 401) { 26 api.dispatch(sessionExpired()); 27 } 28 return result; 29}; 30 31/** Единственный createApi проекта — все entities инжектят сюда */ 32export const baseApi = createApi({ 33 baseQuery: baseQueryWithAuthGuard, 34 reducerPath: "baseApi", 35 endpoints: () => ({}), 36 tagTypes: Object.values(ENUM_API_TAGS) 37});

04Внедрение Эндпоинтов (Entities Layer)

Финальный этап в целевой сущности. Мы импортируем baseApi и вызываем injectEndpoints. Этот код и становится публичным сервисом сущности.

entities/booking/order/api/booking-order.service.ts
1import { baseApi, ENUM_API_TAGS } from "@/shared/api/backend"; 2 3export const bookingOrderApi = baseApi.injectEndpoints({ 4 endpoints: (builder) => ({ 5 getBookingOrders: builder.query<TResponse, TFilters>({ 6 query: (filters) => ({ url: "/booking/orders" }), 7 providesTags: [ENUM_API_TAGS.BOOKING_ORDERS] 8 }) 9 }) 10});