React Query Cache Utilities

SkillDev tools

Use TanStack React Query with auto-generated hooks from @repo/api-client (preferred) or a centralized QueryKeys enum and cache utility for manual setup

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the React Query Cache Utilities skill

What this skill tells your AI

The instructions your AI receives, as published by cliqrelay/cliqrelay in .agents/skills/frontend/react-query-cache-utilities/SKILL.md and read by ahel’s review.

This skill covers @tanstack/react-query.

This project uses React Query (@tanstack/react-query) for server state caching. There are two patterns:

  1. Auto-generated hooks from @repo/api-client — preferred for covered domains
  2. Manual setup — for domains not covered by @repo/api-client

Pattern A: Auto-generated Hooks from @repo/api-client (Preferred)

For domains covered by @repo/api-client, all React Query hooks are auto-generated by Orval:

// Extension app — in a component
import { useGetAllGuides, useCreateGuide, useDeleteGuide } from "@repo/api-client";

function GuideList() {
  // Query hook with auto-generated query keys, caching, and refetching
  const { data, isLoading, error } = useGetAllGuides();

  // Mutation hook with auto-generated invalidate logic
  const deleteMutation = useDeleteGuide({
    mutation: {
      onSuccess: () => {
        showToastSuccess("Guide deleted");
      },
      onError: (error) => {
        showToastError("Error", error.message);
      },
    },
  });

  // ...
}

✅ DO — Use auto-generated hooks as the primary pattern

✅ DO — Use standard TanStack Query patterns (loading, error, success callbacks)

❌ DON'T — Write manual query hooks for domains covered by @repo/api-client


Pattern B: Manual Setup (Non-api-client Domains)

For domains not covered by @repo/api-client, use manual React Query setup:

  • useQuery / useMutation — for component-level data fetching
  • fetchCachedDataOrFetchNewQuery — for app boot hydration into Zustand stores
  • QueryKeys — centralized enum for all cache keys

Folder Structure

constants/
├── query-keys.ts        ← QueryKeys enum ONLY
├── query-client.ts      ← QueryClient instance + cache utility
└── env.ts

QueryClient Setup (constants/query-client.ts)

import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient();

QueryKeys Enum (constants/query-keys.ts)

All cache keys are centralized in a single enum:

// constants/query-keys.ts
export enum QueryKeys {
  GET_USER_ME = "get_user_me",
  INVENTORY = "inventory",
  PRODUCT = "product",
  PRODUCTS = "products",
  // ...
}

✅ DO — All query keys in the enum, no inline string literals

❌ DON'T — Use string literals as query keys

// ❌ DON'T
useQuery(["get_user_me"], getUserMe);

// ✅ DO
useQuery([QueryKeys.GET_USER_ME], getUserMe);

Cache Utility (constants/query-client.ts)

This utility is used specifically for app boot hydration — loading cached or fresh data into Zustand stores before the first render:

// constants/query-client.ts
export const fetchCachedDataOrFetchNewQuery = async <T>(
  queryKey: QueryKey,
  queryFn: () => Promise<unknown>
): Promise<T | null> => {
  const cachedData: any | undefined = queryClient.getQueryData(queryKey);

  if (cachedData) {
    return cachedData as T;
  }

  const fetchedQueryData: any = await queryClient.fetchQuery({
    queryKey,
    queryFn: async () => {
      try {
        return await queryFn();
      } catch (error: any) {
        console.error(error);
        return null;
      }
    },
    staleTime: 60 * 1000
  });

  return fetchedQueryData;
};

Usage: Boot Hydration into Zustand Stores

async function bootstrapApp() {
  const userData = await fetchCachedDataOrFetchNewQuery(
    [QueryKeys.GET_USER_ME],
    getCurrentUser
  );

  if (userData) {
    useSessionStore.getState().setUser(userData);
  } else {
    useSessionStore.getState().setUser(null);
  }
}

When to Use What

ScenarioPatternTool
Extension app, api-client covers the domainAuto-generated hooksuseGetAllGuides, useCreateGuide, etc.
Web app, client-side caching neededStandard React QueryuseQuery / useMutation
App boot, hydrate store (non-api-client)Manual cache utilityfetchCachedDataOrFetchNewQuery
Invalidating cache (non-api-client)ManualqueryClient.invalidateQueries()

Rules

✅ DO

  • Use auto-generated hooks from @repo/api-client for covered domains
  • Put QueryKeys enum in constants/query-keys.ts (non-api-client domains)
  • Put QueryClient + fetchCachedDataOrFetchNewQuery in constants/query-client.ts (non-api-client domains)
  • Use fetchCachedDataOrFetchNewQuery only for app boot / context provider hydration
  • Cache-first: always check cache before network

❌ DON'T

  • Don't write manual query hooks for domains covered by @repo/api-client
  • Don't use fetchCachedDataOrFetchNewQuery in components — it's for boot hydration only
  • Don't inline query key strings — always use QueryKeys
  • Don't forget staleTime when using fetchCachedDataOrFetchNewQuery

Signals

GitHub stars
40
Forks
1
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
react-query-cache-utilities
Source
github.com/cliqrelay/cliqrelay