data-fetching
Серверное состояние на TanStack Query (React Query) поверх реального API бэкенда. Источник истины — OpenAPI-спека бэка (Fastify/NestJS/Express и др.): типы генерируются из спеки (openapi-typescript),
SKILL.mdskills/data-fetching/SKILL.md
GitHubНавык: Data fetching (TanStack Query + OpenAPI)
Серверное состояние — на TanStack Query поверх реального API бэкенда. Клиентское UI-состояние — в Zustand (скилл state-management).
Правило №1: всегда реальный API, источник истины — OpenAPI-спека бэка
- Фронт всегда ходит в реальные эндпоинты бэка. Никаких моков в прод-пути (MSW — только в тестах, скилл
react-testing). - Единый источник правды контракта — OpenAPI-спека бэка (эндпоинт спеки под стек:
/documentation/jsonу Fastify,/api-jsonу NestJS,/openapi.jsonи т.п.). Полнота спеки — забота бэка (скиллswagger-coverage). - Типы клиента генерируются из спеки, а не пишутся руками и не дублируют бэк.
Типизированный клиент (openapi-typescript + openapi-fetch)
- Сгенерируй типы из спеки в
src/shared/api/schema.d.ts(держи как скриптpnpm gen:api, перегенерируй при изменении бэка):npx openapi-typescript http://localhost:3000/documentation/json -o src/shared/api/schema.d.ts - Один клиент в
src/shared/api/client.ts:
Пути, query, body и ответы проверяются на типах против спеки — рассинхрон фронта и бэка ловит компилятор.import createClient from 'openapi-fetch' import type { paths } from './schema' export const api = createClient<paths>({ baseUrl: process.env.NEXT_PUBLIC_API_URL })
Слой api фичи
queryFn/mutationFnвызываютapi.GET/POST/...по эндпоинту из спеки; валидацию ответа обеспечивает бэк (сериализация по response-схеме, напр. в Fastify), на фронте достаточно типов из спеки.- Фабрику ключей держи в
features/<feature>/api— единый источник правды для инвалидации.
Ключи запросов
- Иерархические ключи-массивы:
['orders'],['orders', id],['orders', { status }]. - Локаль в ключах: если ответ зависит от языка (локализованный контент с бэка), включай локаль в
queryKey(['orders', { locale }]) и шлиAccept-Language, чтобы кэш не смешивал языки. См. скилл i18n.
Запросы
useQuery({ queryKey, queryFn }); состоянияisLoading/isError/dataобрабатывай явно (loading/empty/error UI).- Настрой
staleTime/gcTimeосознанно;placeholderData/keepPreviousDataдля пагинации.
Мутации
useMutation({ mutationFn, onSuccess }); после успеха —queryClient.invalidateQueries({ queryKey }).- Оптимистичные обновления:
onMutate(снимок + оптимистичный set),onError(откат),onSettled(инвалидация).
Интеграция с Next.js
- Фетч на сервере (RSC/Server Actions) + гидрация:
prefetchQuery→dehydrate/HydrationBoundary(тем же клиентомapi). - Мутации через Server Actions, затем инвалидация клиентского кэша Query.
- Один
QueryClientна запрос на сервере; на клиенте — стабильный инстанс вsrc/lib.
Пример
import { api } from '@/shared/api/client';
export const ordersKeys = { all: ['orders'] as const, detail: (id: string) => ['orders', id] as const };
const fetchOrders = async () => {
const { data, error } = await api.GET('/orders');
if (error) throw error;
return data;
};
export function useOrders() {
return useQuery({ queryKey: ordersKeys.all, queryFn: fetchOrders });
}