feat(docs-cache): purge Netlify CDN tags on invalidation#932
Conversation
The DB-level SWR fix only refreshes the cached GitHub content rows;
the Netlify CDN sits in front of the SSR with its own 10min/1d SWR
windows, so clicking "invalidate" or receiving a push webhook still
left stale rendered pages in front of users for up to ~10 minutes.
Attach Netlify-Cache-Tag headers to the docs route and the
getTanstackDocsConfig server function with three tiers
(docs:all, docs:{libId}, docs:{libId}:branch:{resolvedBranch}) so we
can purge at any granularity. Then issue purgeCache calls from both
invalidateDocsCacheAdmin and the push webhook after the existing DB
mark calls.
Purge keys off the resolved branch (via getBranch), so a single push
invalidates the latest/v5/main URL variants together. Failures are
logged + sent to Sentry but never thrown - the DB rows are already
stale and SWR will eventually catch up. Falls back to a silent no-op
locally when SITE_ID / NETLIFY_PURGE_API_TOKEN are absent.
📝 WalkthroughWalkthroughThis PR implements tag-based cache invalidation for Netlify by introducing a purge utility, tagging cached responses with library and branch identifiers, and integrating purge calls into GitHub webhook and admin invalidation flows. ChangesNetlify Cache Tag-Based Invalidation
🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/routes/api/github/webhook.ts`:
- Around line 141-149: The purge tag generation currently filters libraries with
library.latestBranch === gitRef which prevents branch pushes from producing CDN
purge tags; in the tags construction (the array using repo, gitRef and
libraries) remove the latestBranch check so that any library where library.repo
=== repo generates a `docs:${library.id}:branch:${gitRef}` tag (i.e., use
libraries.filter(l => l.repo === repo).map(...) instead of also requiring
latestBranch), ensuring pushes to non-latest branches also emit purge tags.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4de71a8d-7af0-40f8-a34c-b8ba5ef5eb59
📒 Files selected for processing (5)
src/routes/$libraryId/$version.docs.$.tsxsrc/routes/api/github/webhook.tssrc/utils/config.tssrc/utils/docs-admin.server.tssrc/utils/netlify-purge.server.ts
Summary
Netlify-Cache-Tagheaders to the docs route (/$libraryId/$version/docs/$) and thegetTanstackDocsConfigserver function so the CDN can be purged surgically.purgeCache({ tags })frominvalidateDocsCacheAdminand the GitHub push webhook after the existing DB-mark calls.Why
The DB SWR-on-forced-stale fix only refreshes the cached GitHub content rows. The Netlify CDN still sits in front of the SSR with its own SWR windows (
max-age=600, stale-while-revalidate=3600for latest,86400/604800for older versions, plusmax-age=300, stale-while-revalidate=300on the docs config). So even after marking rows stale, an operator clicking "invalidate" — or a real push — still had to wait up to ~10 minutes for the CDN to revalidate the rendered pages. This change closes that window.Tag scheme
Three tiers so we can purge at any granularity:
Docs route —
docs:all, docs:{libraryId}, docs:{libraryId}:branch:{resolvedBranch}Docs config server fn —
docs-config:all, docs-config:{repo}, docs-config:{repo}:{branch}The library-level tag keys off the resolved branch (via
getBranch), not the URLversion, so a single push invalidates thelatest/v5/mainURL variants together.Purge behavior
(repo, gitRef)) →docs-config:{repo}:{gitRef}+docs:{libId}:branch:{gitRef}for each matched library.repo→docs-config:{repo}+docs:{libId}for each matched library.repo→docs:all+docs-config:all.Failure handling
purgeNetlifyTagsinsrc/utils/netlify-purge.server.tsnever throws:SITE_IDorNETLIFY_PURGE_API_TOKENis absent (local dev).{ purge: { purged, ... } }) so the admin UI can surface it.Operational note
NETLIFY_PURGE_API_TOKENandSITE_IDare typically auto-injected by Netlify into Functions at runtime — usually nothing to configure. If a manual purge from the admin button returns{ purge: { purged: false, reason: 'no-credentials' } }, generate a personal access token withSite:Purgescope and set it asNETLIFY_PURGE_API_TOKENin the site env.Test plan
pnpm test:tscpassespnpm test:lintpasses (0 errors)pnpm test:smokepasses (all docs/blog routes render)purge: { purged: true, tags: [...] }and that the docs route serves a fresh response within seconds.NETLIFY_PURGE_API_TOKEN; if not, mint one withSite:Purgescope.Summary by CodeRabbit
New Features
Improvements