Детальное руководство по архитектуре RTK Query (Code Splitting).
Архитектурный инвариант: createApi вызывается один раз — в shared/api/backend/base.api.ts. Все сущности внедряют эндпоинты в единый baseApi (один кэш, один реестр тегов). Отдельный createApi допустим только для внешнего сервиса с другим baseUrl.
Для корректной работы инвалидации providesTags / invalidatesTags, все возможные теги собираются в централизованный конфиг.
1export const ENUM_API_TAGS = {
2 USER: "User",
3 BOOKING_ORDERS: "Booking Orders",
4 // и другие теги...
5} as const;Action объявлен в shared, поэтому baseApi не зависит от entities. Auth slice подписывается на него через extraReducers и сбрасывает сессию.
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// }Единственный createApi в проекте. Его base query оборачивает fetchBaseQuery: при 401 диспатчит sessionExpired() из shared, не импортируя entities.
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});Финальный этап в целевой сущности. Мы импортируем baseApi и вызываем injectEndpoints. Этот код и становится публичным сервисом сущности.
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});