Cutting API Calls by Up to 80% with Smarter React Query Caching
Cutting API Calls by Up to 80% with Smarter React Query Caching
Version: 1.0.44
Category: Performance
The Problem
Prior to v1.0.44, the tRPC QueryClient was created with no configuration:
// Before — no defaultOptions
const queryClient = new QueryClient()
React Query's out-of-the-box staleTime is 0. This means every query is treated as stale the moment it resolves — so every time a user navigates back to a page, React Query immediately discards the cached data and fires a new network request.
In practice, returning to the dashboard triggered 5 or more API calls on every navigation, hitting endpoints that serve data that barely ever changes:
availableTaxYears— changes once a year at mostcategoryMeta— static reference dataconnectedAgents— changes only when the user explicitly connects or disconnects an agent
This made the application feel slower than necessary and placed avoidable load on the server.
The Fix
The QueryClient in src/lib/trpc/provider.tsx is now initialised with explicit caching defaults:
// After — sensible caching defaults
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 30_000, // data is fresh for 30 seconds
gcTime: 300_000, // unused cache entries are kept for 5 minutes
},
},
})
What these values mean
| Option | Value | Effect |
|---|---|---|
staleTime | 30 seconds | Cached data is considered fresh for 30 s after it was last fetched. Navigating back to a page within that window skips the network request entirely. |
gcTime | 5 minutes | Inactive cache entries are held in memory for 5 minutes before being garbage-collected, so returning to a page after a short absence still benefits from the cached value. |
Per-query overrides for stable data
For data that changes very infrequently, a longer staleTime of 1 hour is applied at the query level:
availableTaxYears— the set of HMRC tax years available for submission changes at most once a year.categoryMeta— category definitions are static reference data and do not change between user sessions.
This means a user can navigate freely around the application for an entire session without these endpoints being hit more than once.
Impact
60–80% reduction in API calls during normal navigation.
This is the single most impactful frontend performance change in the codebase. The improvement is felt immediately — the dashboard loads faster on return visits, and the server handles significantly less traffic from routine page transitions.
Notes for Developers
- The global
staleTime: 30_000default applies to all tRPC queries unless overridden. - To set a per-query staleTime when using tRPC + React Query, pass
staleTimeinside the query options:
trpc.availableTaxYears.useQuery(undefined, {
staleTime: 60 * 60 * 1000, // 1 hour
})
gcTime(formerlycacheTimein React Query v4) controls how long inactive (unmounted) query data is kept in memory. It does not affect whether a query is considered stale — that is controlled solely bystaleTime.- If a query must always reflect the latest server state (e.g. live submission status), you can opt out by setting
staleTime: 0on that specific query.