| license | Apache-2.0 |
| name | data-fetching-strategist |
| description | Architect data fetching with TanStack Query v5, SWR, optimistic updates, prefetching, and cache invalidation strategies. Activate on: React Query, SWR, optimistic updates, cache invalidation, prefetching, stale-while-revalidate, infinite queries. NOT for: REST API design (use api-architect), database queries (use database skills), GraphQL schema (use graphql-expert). |
| allowed-tools | Read,Write,Edit,Bash(npm:*,npx:*) |
| category | Frontend & UI |
| tags | ["tanstack-query","data-fetching","caching","optimistic-updates","prefetching"] |
| pairs-with | [{"skill":"react-performance-optimizer","reason":"Smart caching and prefetching directly improve perceived performance"},{"skill":"react-server-components-expert","reason":"RSC changes data fetching patterns -- server fetch vs. client query decisions"}] |
Data Fetching Strategist
Decision Points
1. Library Selection: TanStack Query vs SWR vs Manual
Data Complexity?
├─ Simple GET requests, minimal caching needs
│ └─ Use SWR: lighter bundle, simpler API
├─ Complex mutations, optimistic updates, prefetching
│ └─ Use TanStack Query v5: full-featured
└─ Static data, build-time fetching
└─ Use manual fetch in RSC/getStaticProps
2. Cache Strategy Configuration
Data Freshness Requirements?
├─ Real-time (user notifications, live chat)
│ ├─ staleTime: 0, refetchInterval: 5000
│ └─ Consider WebSocket instead
├─ Frequent updates (user profiles, dashboards)
│ └─ staleTime: 30000 (30s), gcTime: 300000 (5m)
├─ Occasional updates (product catalogs, articles)
│ └─ staleTime: 300000 (5m), gcTime: 600000 (10m)
└─ Static data (countries, categories)
└─ staleTime: Infinity, gcTime: Infinity
3. Mutation Error Recovery
Mutation Failed?
├─ Network error (offline, timeout)
│ ├─ Show "offline" indicator
│ ├─ Queue mutation for retry when online
│ └─ Keep optimistic update visible
├─ Validation error (400, 422)
│ ├─ Rollback optimistic update immediately
│ ├─ Show field-level errors
│ └─ Focus first error field
├─ Authorization error (401, 403)
│ ├─ Rollback optimistic update
│ ├─ Clear auth tokens
│ └─ Redirect to login
└─ Server error (500, 503)
├─ Rollback optimistic update
├─ Show generic error message
└─ Auto-retry up to 3 times
4. Query Key Design
Invalidation Scope Needed?
├─ Invalidate all related data (user updates profile)
│ └─ Hierarchical: ['users', userId, 'profile']
│ └─ Allows: invalidateQueries(['users', userId])
├─ Invalidate specific subset (filter changes)
│ └─ Include filters: ['products', 'list', { category, sort }]
│ └─ Allows: invalidateQueries(['products', 'list'])
└─ Never invalidate together
└─ Separate top-level: ['analytics'], ['notifications']
5. Prefetching Triggers
Navigation Pattern?
├─ Mouse-driven (hover to preview)
│ └─ prefetchQuery on onMouseEnter + onFocus
├─ Swipe/touch interface
│ └─ prefetchQuery on visible list items (intersection observer)
├─ Predictable flow (multi-step form)
│ └─ prefetchQuery on current step completion
└─ Route-based preloading
└─ prefetchQuery in route loader/middleware
Failure Modes
1. Race Condition Chaos
Symptoms: UI shows wrong data after rapid clicks, mutations overwrite each other
Detection: User clicks fast → sees data flicker → final state doesn't match last action
Fix:
: (variables) => {
queryClient.({ queryKey });
previous = queryClient.(queryKey);
queryClient.(queryKey, optimisticUpdate);
{ previous };
}