From harness-claude
Configures RTK Query with createApi and fetchBaseQuery for automatic caching, deduplication, and loading states in Redux Toolkit apps. Use for data fetching, replacing thunks, and CRUD interfaces.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Configure RTK Query with createApi and fetchBaseQuery for automatic caching, deduplication, and loading state management
Defines RTK Query endpoints for API queries and mutations using builder.query/mutation, with cache tags, invalidation, response transforms, and auto-generated hooks.
Guides Redux and Redux Toolkit for global state management: slices, stores, actions, reducers, hooks, selectors, middleware, async thunks.
Provides React Query (TanStack Query v5) and Zustand v5 patterns for data fetching, caching, mutations, optimistic updates, and client-side state management. Use for useQuery, useMutation, Zustand stores, or caching tasks.
Share bugs, ideas, or general feedback.
Configure RTK Query with createApi and fetchBaseQuery for automatic caching, deduplication, and loading state management
services/api.ts or api/baseApi.ts.fetchBaseQuery as the base query — it wraps fetch with sensible defaults and automatic header handling.baseUrl to the API root. Use prepareHeaders to inject auth tokens.tagTypes upfront for all cacheable entity types — these drive automatic invalidation.endpoints builder and inject endpoints from feature slices using injectEndpoints.[api.reducerPath] and add api.middleware to the middleware chain.// services/api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: '/api/v1',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token;
if (token) headers.set('Authorization', `Bearer ${token}`);
return headers;
},
}),
tagTypes: ['User', 'Post', 'Comment'],
endpoints: () => ({}), // Injected by feature modules
});
// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import { api } from '../services/api';
export const store = configureStore({
reducer: {
[api.reducerPath]: api.reducer,
// ...other slices
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware),
});
Why one API per base URL: RTK Query normalizes cache keys per API instance. Multiple APIs hitting the same backend fragment the cache and break invalidation. Split APIs only for genuinely different backends.
fetchBaseQuery vs custom baseQuery: fetchBaseQuery handles JSON serialization, error normalization, and timeout. For custom needs (Axios, retry logic, reauthentication), wrap it:
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await fetchBaseQuery({ baseUrl: '/api' })(args, api, extraOptions);
if (result.error?.status === 401) {
const refreshResult = await fetchBaseQuery({ baseUrl: '/api' })(
{ url: '/auth/refresh', method: 'POST' },
api,
extraOptions
);
if (refreshResult.data) {
result = await fetchBaseQuery({ baseUrl: '/api' })(args, api, extraOptions);
}
}
return result;
};
Endpoint injection pattern: Keeps the base API slim and lets features own their endpoints:
// features/users/users.api.ts
import { api } from '../../services/api';
const usersApi = api.injectEndpoints({
endpoints: (builder) => ({
getUsers: builder.query({ query: () => '/users', providesTags: ['User'] }),
}),
});
export const { useGetUsersQuery } = usersApi;
Common mistakes:
api.middleware (breaks polling, cache lifetime, and refetchOnFocus)reducerPath that does not match the store keycreateApi instances for the same backendhttps://redux-toolkit.js.org/rtk-query/overview