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

Data Management via RTK Query

Detailed guide on RTK Query architecture (Code Splitting).

RTK Query Code Splitting Architecture

Architectural Invariant: createApi is called only once — in shared/api/backend/base.api.ts. All entities inject their endpoints into this single baseApi (one cache, one tag registry). A separate createApi is allowed only for an external service with a different baseUrl.

API Core File Structure

01Global Tag Registry (Shared Layer)

For correct operation of invalidation providesTags / invalidatesTags, all possible tags are collected in a centralized config.

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

02Session Expired Action (Shared Layer)

An action declared in shared, so baseApi does not depend on entities. The auth slice subscribes to it via extraReducers and resets the session.

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// }

03Base API Instance with Auth Guard (Shared Layer)

The only createApi in the project. Its base query wraps fetchBaseQuery: on 401 it dispatches sessionExpired() from shared, without importing 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});

04Endpoint Injection (Entities Layer)

The final stage in the target entity. We import baseApi and call injectEndpoints. This code becomes the public service of the entity.

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});