Серверное состояние на TanStack Query поверх типобезопасного клиента из OpenAPI; кэш, мутации и инвалидация без production mocks.
Scanned 9/11/2026
Install to Claude Code
npx -y skills add Vitammiin/agent-vorcl-flow --skill data-fetching --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Data Fetching?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/vitammiin-data-fetching)More formats (shields.io, HTML) on the badges page.
---
name: data-fetching
description: "Серверное состояние на TanStack Query поверх типобезопасного клиента из OpenAPI; кэш, мутации и инвалидация без production mocks."
---
# Навык: 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)
- Генерация типов (скрипт `pnpm gen:api`, перегенерировать при изменении бэка): `npx openapi-typescript http://localhost:3000/documentation/json -o src/shared/api/schema.d.ts`.
- Один клиент в `src/shared/api/client.ts`:
```ts
import createClient from 'openapi-fetch'
import type { paths } from './schema'
export const api = createClient<paths>({ baseUrl: process.env.NEXT_PUBLIC_API_URL })
```
Пути/query/body/ответы проверяются на типах против спеки — рассинхрон ловит компилятор.
## Слой 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` обрабатывай явно.
- Осознанные `staleTime`/`gcTime`; `placeholderData`/`keepPreviousData` для пагинации.
## Мутации
- `useMutation`; после успеха — `invalidateQueries({ queryKey })`.
- Оптимистично: `onMutate` (снимок + set), `onError` (откат), `onSettled` (инвалидация).
## Интеграция с Next.js
- `prefetchQuery` → `dehydrate`/`HydrationBoundary` (тем же клиентом `api`).
- Мутации через Server Actions + инвалидация кэша Query.
- Один `QueryClient` на запрос на сервере; стабильный инстанс на клиенте в `src/lib`.
## Пример
```ts
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 });
}
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!