src/skills/web-server-state-react-query/SKILL.md
React Query server state, hey-api OpenAPI codegen, type-safe data fetching
npx skillsauth add agents-inc/skills web-server-state-react-queryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Generate type-safe React Query hooks from OpenAPI specs using hey-api. Never write custom query hooks or manual type definitions -- use generated query options (
getFeaturesOptions()pattern) and generated types. Configure the client once via environment variables. All timeouts/retries use named constants.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use generated query options from hey-api -- NEVER write custom React Query hooks)
(You MUST regenerate client code when OpenAPI schema changes)
(You MUST use named constants for ALL timeout/retry values -- NO magic numbers)
(You MUST configure API client base URL via environment variables)
</critical_requirements>
Auto-detection: OpenAPI schema, hey-api, openapi-ts, generated React Query hooks, query options, getFeaturesOptions, useQuery, useMutation, QueryClient, QueryClientProvider, staleTime, gcTime, queryKey
When to use:
When NOT to use:
OpenAPI-first development ensures a single source of truth for your API contract. The hey-api code generator (@hey-api/openapi-ts) transforms your OpenAPI schema into fully typed client code, React Query hooks, and query options -- eliminating manual type definitions and reducing bugs.
Core Principles:
Configure @hey-api/openapi-ts to generate TypeScript client code and React Query hooks from your OpenAPI spec. Since v0.73.0, client packages are bundled -- no separate installation needed.
// openapi-ts.config.ts
import { defineConfig } from "@hey-api/openapi-ts";
export default defineConfig({
input: "./openapi.yaml",
output: "src/api-client",
plugins: [
"@hey-api/typescript",
"@hey-api/sdk",
"@tanstack/react-query",
// "@hey-api/client-fetch" -- optional, Fetch is the default client since v0.73
],
});
Key points: @hey-api/typescript generates types (renamed from @hey-api/types), @hey-api/sdk generates service functions (renamed from @hey-api/services). Fetch client is bundled by default since v0.73 -- only add @hey-api/client-fetch explicitly to customize its options. Run generation via npx openapi-ts or add as a build script.
See examples/core.md Pattern 1 for generated output structure and usage.
Configure the API client base URL and QueryClient defaults once in a provider component. Use environment variables for the base URL so it works across environments without code changes.
const FIVE_MINUTES_MS = 5 * 60 * 1000;
// In your provider component:
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: { staleTime: FIVE_MINUTES_MS, refetchOnWindowFocus: false },
},
}),
);
client.setConfig({ baseUrl: process.env.API_BASE_URL ?? "" });
Key points: hey-api's client.setConfig() merges with existing config (doesn't replace). Named constants for all time values. Set auth option or use interceptors for auth headers.
See examples/core.md Pattern 2 for full provider setup and auth configuration.
Use generated query options directly -- never write custom React Query hooks. Options are fully typed and include generated query keys.
import { useQuery } from "@tanstack/react-query";
import { getFeaturesOptions } from "./api-client/@tanstack/react-query.gen";
// Direct usage -- fully typed
const { data, isPending, error } = useQuery(getFeaturesOptions());
// With overrides -- spread and customize
const TEN_MINUTES_MS = 10 * 60 * 1000;
const { data } = useQuery({
...getFeaturesOptions(),
staleTime: TEN_MINUTES_MS,
enabled: someCondition,
});
Why good: Zero boilerplate, type-safe, consistent patterns, query keys auto-namespaced, easy to customize by spreading
See examples/core.md Pattern 3 for component examples and bad patterns to avoid.
React Query v5 removed onError/onSuccess/onSettled callbacks from useQuery. Use component-level isPending/error states, useEffect for error side effects, or global handlers via QueryCache/MutationCache.
// Global error handling (v5 pattern)
new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => {
if (query.state.data !== undefined) {
showNotification(`Something went wrong: ${error.message}`);
}
},
}),
mutationCache: new MutationCache({
onError: (error) => {
showNotification("Operation failed. Please try again.");
},
}),
});
See examples/error-handling.md for component-level handling, retry with exponential backoff, and error boundaries.
Debounce search/filter queries to prevent excessive API calls on every keystroke.
const DEBOUNCE_DELAY_MS = 500;
const MIN_SEARCH_LENGTH = 0;
const debouncedTerm = useDebounce(searchTerm, DEBOUNCE_DELAY_MS);
const { data } = useQuery({
queryKey: ["search", debouncedTerm],
queryFn: () => searchAPI(debouncedTerm),
enabled: debouncedTerm.length > MIN_SEARCH_LENGTH,
});
Why good: Prevents excessive API calls, query key includes debounced term for proper cache management, enabled prevents empty queries
Detailed Resources:
<red_flags>
High Priority Issues:
FIVE_MINUTES_MS, MAX_RETRY_ATTEMPTS)onError/onSuccess callbacks on useQuery -- removed in React Query v5Medium Priority Issues:
retry: true in development with mocks -- should be false to fail fastGotchas & Edge Cases:
cacheTime was renamed to gcTime in v5 (garbage collection time)isLoading was renamed to isPending in v5keepPreviousData replaced with placeholderData: (prev) => prevuseInfiniteQuery now requires initialPageParam optionclient.setConfig() merges with existing config, doesn't replace itstaleTime/gcTime</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST use generated query options from hey-api -- NEVER write custom React Query hooks)
(You MUST regenerate client code when OpenAPI schema changes)
(You MUST use named constants for ALL timeout/retry values -- NO magic numbers)
(You MUST configure API client base URL via environment variables)
Failure to follow these rules will cause type drift, inconsistent patterns, and production bugs.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety