Skip to content

[Performance] Per-endpoint SWR cache TTL + immediate mutation invalidation#743

Merged
RUKAYAT-CODER merged 5 commits into
rinafcode:mainfrom
Spagero763:feat/per-endpoint-cache-ttl-597
Jun 29, 2026
Merged

[Performance] Per-endpoint SWR cache TTL + immediate mutation invalidation#743
RUKAYAT-CODER merged 5 commits into
rinafcode:mainfrom
Spagero763:feat/per-endpoint-cache-ttl-597

Conversation

@Spagero763

Copy link
Copy Markdown
Contributor

[Performance] Per-endpoint SWR cache TTL + immediate mutation invalidation

Closes #597

Problem

The SWR cache used a single global TTL, so volatile data (subscription status,
balance, active quiz session) shared a freshness window with static catalog data.
A user who upgraded could see the old "Free" status for the full TTL.

Changes

  • src/config/apiCacheConfig.ts
    • New ENDPOINT_TTL_MAP: Record<string, EndpointTtl> plus resolveEndpointTtl(url):
      • Critical endpoints (/auth/me, /subscriptions, /payments): 30 s TTL,
        with staleTtl === ttl so nothing older than 30 s is ever served.
      • Static endpoints (/courses, /categories): 5 min TTL.
      • Falls back to the global default (60 s / 5 min) for unconfigured endpoints.
      • URL normalization handles /api prefixes, query strings, api: cache-key
        prefixes, and nested paths (longest-prefix match).
    • Added /subscriptions and /payments rules to MUTATION_INVALIDATION_MAP so a
      write to either immediately invalidates the related GET caches (including
      /auth/me, which embeds subscription status).
  • src/services/api/cache.ts — added invalidatePattern(urlPattern) (public
    alias of the existing invalidateByPattern), the method named in the issue.
  • src/services/api/index.tsapiService.get now resolves the per-endpoint
    TTL via resolveEndpointTtl(url) before falling back to the global default, and
    re-exports invalidatePattern / resolveEndpointTtl / ENDPOINT_TTL_MAP.
  • src/__tests__/issues/fix_597_cache_ttl.test.ts — 8 unit tests (all passing).

The axios response interceptor already iterates MUTATION_INVALIDATION_MAP and
calls invalidateByPattern after successful mutations, so adding the
subscriptions/payments rules wires up the immediate invalidation end-to-end.

Acceptance criteria

  • /subscriptions cache expires after 30 seconds maximum (ttl = staleTtl = 30s).
  • /courses cache valid for 5 minutes.
  • POST to /subscriptions immediately invalidates the /subscriptions GET cache.
  • Unit test confirms different TTL values applied per endpoint.

Verification

  • jest src/__tests__/issues/fix_597_cache_ttl.test.ts8/8 passing.
  • eslint --max-warnings=0 is clean on all changed files.
  • tsc --noEmit adds no new errors from the changed files.

Note: bundled lint fix (required to commit)

The repo's eslint.config.js had 'no-console': ['error', { allow: [] }], which the
pinned ESLint 9.39.4 rejects (allow must have ≥1 item). This crashed expo lint
and the husky pre-commit hook on a clean checkout, so no commit could pass the
hook. I fixed it to the equivalent valid form 'no-console': 'error' (same intent:
disallow all console.*) in a separate, clearly-labeled commit so the lint/hook run
again. Happy to split this into its own PR if preferred.

@drips-wave

drips-wave Bot commented Jun 29, 2026

Copy link
Copy Markdown

@Spagero763 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project.

@RUKAYAT-CODER RUKAYAT-CODER merged commit ec98225 into rinafcode:main Jun 29, 2026
1 of 10 checks passed
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.

[Performance] SWR cache TTL not configurable per endpoint — critical data served stale

2 participants