Skip to content

Persist React Query cache to localStorage to soften the unauth API ceiling#10

Merged
kanywst merged 2 commits into
mainfrom
feat/persist-query-cache
May 6, 2026
Merged

Persist React Query cache to localStorage to soften the unauth API ceiling#10
kanywst merged 2 commits into
mainfrom
feat/persist-query-cache

Conversation

@kanywst
Copy link
Copy Markdown
Owner

@kanywst kanywst commented May 6, 2026

Summary

Unauthenticated GitHub API gives 60 req/h, and a hard refresh of stardust currently fans out to 10 languages × (1 preview + sometimes 1 full) requests, so a couple of reloads is enough to start hitting 403s. Authenticating client-side from a static GitHub Pages bundle is not safe (any token in the bundle is public), so the practical lever is to stop re-fetching things we already have.

This wraps the app in PersistQueryClientProvider from @tanstack/react-query-persist-client and a localStorage persister:

  • 24h maxAge — successful responses survive reloads for a day
  • gcTime bumped to match maxAge; otherwise the in-memory cache evicts entries before persistence can rehydrate them
  • buster: 'v1' so we can invalidate stored caches if the schema changes
  • 1 new prod dep + 1 transitive (@tanstack/query-sync-storage-persister, @tanstack/react-query-persist-client); bundle goes from 370KB → 374KB (+118KB → +118KB gzipped, no real movement)

Per-query staleTime: 1h is unchanged, so the freshness story is the same — this only changes what happens between sessions and across hard reloads.

Test plan

  • npm run lint
  • npm audit --omit=dev --audit-level=high
  • npm run build
  • npm run test -- --run
  • manual: load the site, reload immediately, confirm cards render from cache without a network round trip in DevTools
  • manual: clear stardust-query-cache from localStorage, reload, confirm fresh fetch

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Warning

Rate limit exceeded

@kanywst has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 20 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: deebb2f6-7984-4ccb-92a5-4750505d026e

📥 Commits

Reviewing files that changed from the base of the PR and between ca5040f and 1814955.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json
  • src/App.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/persist-query-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements client-side persistence for React Query by adding the @tanstack/react-query-persist-client and @tanstack/query-sync-storage-persister dependencies. The App component was updated to use PersistQueryClientProvider with a localStorage persister and a 24-hour garbage collection time. Feedback was provided to wrap the window.localStorage access in a try...catch block to ensure the application remains robust in environments where storage access might be restricted or disabled.

I am having trouble creating individual review comments. Click here to see my feedback.

src/App.tsx (22-25)

high

Accessing window.localStorage can throw an exception in certain environments, such as when a user has disabled storage for the site or is using a browser in a privacy mode that restricts storage access. This could cause the application to crash on startup for some users.

To make the application more robust, it's best to wrap the access to window.localStorage in a try...catch block. If localStorage is unavailable, you can pass undefined to createSyncStoragePersister, which will gracefully disable persistence without crashing the app.

const persister = createSyncStoragePersister({
  storage: (() => {
    try {
      return window.localStorage;
    } catch {
      return undefined;
    }
  })(),
  key: 'stardust-query-cache',
});

GitHub's unauth API quota (60 req/h) is the practical bottleneck for
this site, and a single hard refresh hits 10 languages × 1-2 queries.
Wrap the app in PersistQueryClientProvider so successful responses
survive reloads for 24h, falling back to a network request only when
the entry is missing or stale. gcTime is bumped to match maxAge so
entries are not evicted from the in-memory cache before they can be
rehydrated.

The buster string lets future schema changes invalidate stored caches
without manual user cleanup.
@kanywst kanywst force-pushed the feat/persist-query-cache branch from b74e3b8 to df4fb1d Compare May 6, 2026 17:01
…t throw

Older Safari private mode and browsers with site data disabled can
throw on the very first `window.localStorage` property access (not
just on getItem/setItem), which would have crashed the app at boot
under PersistQueryClientProvider. Wrap the access in try/catch and
fall back to undefined — createSyncStoragePersister treats that as
'no persistence' rather than crashing.
@kanywst kanywst merged commit 87cb847 into main May 6, 2026
3 checks passed
@kanywst kanywst deleted the feat/persist-query-cache branch May 6, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant