[PR #917] [MERGED] fix(app-router): Make revalidatePath expire route-scoped fetch cache reads #946

Closed
opened 2026-05-06 13:11:00 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/917
Author: @NathanDrake2406
Created: 4/27/2026
Status: Merged
Merged: 4/27/2026
Merged by: @james-elicx

Base: mainHead: nathan/revalidate-path-fetch-soft-tags


📝 Commits (1)

  • f7aaec5 Make revalidatePath expire route-scoped fetch cache reads

📊 Changes

8 files changed (+215 additions, -62 deletions)

View changed files

📝 packages/vinext/src/cloudflare/kv-cache-handler.ts (+77 -52)
📝 packages/vinext/src/entries/app-rsc-entry.ts (+4 -1)
📝 packages/vinext/src/shims/cache.ts (+13 -0)
📝 packages/vinext/src/shims/fetch-cache.ts (+18 -2)
📝 packages/vinext/src/shims/unified-request-context.ts (+1 -0)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+24 -6)
📝 tests/fetch-cache.test.ts (+22 -1)
📝 tests/kv-cache-handler.test.ts (+56 -0)

📄 Description

What this changes

Route-scoped implicit tags are now threaded into App Router fetch cache reads as soft tags. revalidatePath() can now force cached fetch() results to miss while rendering the affected path, even when the fetch only uses next.revalidate and no explicit next.tags.

Why

Before this change, vinext invalidated the page or route cache entry for a path, but the regenerated render could still reuse an older fetch cache entry because fetch entries only carried explicit next.tags. Next.js treats page-derived implicit tags as read-time soft tags for fetch cache lookup:

Approach

Add request-scoped fetch soft tags and set them from the generated App Router entry after final route matching and inside ISR regeneration scopes. Pass those soft tags to fetch cache reads.

Memory and KV cache handlers now treat revalidated soft tags as read-time misses, but only stored entry tags trigger deletion. This keeps shared fetch entries from being permanently coupled to one route path while still matching the Next.js invalidation decision.

Validation

  • vp run vinext#build
  • vp test run tests/fetch-cache.test.ts tests/kv-cache-handler.test.ts tests/isr-cache.test.ts tests/entry-templates.test.ts
  • vp check

Risks / follow-ups

This PR follows the existing vinext path-tag derivation helper. It does not broaden the scope to unrelated revalidatePath() parity gaps, such as root/index alias handling.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/917 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 4/27/2026 **Status:** ✅ Merged **Merged:** 4/27/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `nathan/revalidate-path-fetch-soft-tags` --- ### 📝 Commits (1) - [`f7aaec5`](https://github.com/cloudflare/vinext/commit/f7aaec50dc2d2e40d7b197f5ec9bffef9a61daec) Make revalidatePath expire route-scoped fetch cache reads ### 📊 Changes **8 files changed** (+215 additions, -62 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/cloudflare/kv-cache-handler.ts` (+77 -52) 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+4 -1) 📝 `packages/vinext/src/shims/cache.ts` (+13 -0) 📝 `packages/vinext/src/shims/fetch-cache.ts` (+18 -2) 📝 `packages/vinext/src/shims/unified-request-context.ts` (+1 -0) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+24 -6) 📝 `tests/fetch-cache.test.ts` (+22 -1) 📝 `tests/kv-cache-handler.test.ts` (+56 -0) </details> ### 📄 Description ## What this changes Route-scoped implicit tags are now threaded into App Router fetch cache reads as soft tags. `revalidatePath()` can now force cached `fetch()` results to miss while rendering the affected path, even when the fetch only uses `next.revalidate` and no explicit `next.tags`. ## Why Before this change, vinext invalidated the page or route cache entry for a path, but the regenerated render could still reuse an older fetch cache entry because fetch entries only carried explicit `next.tags`. Next.js treats page-derived implicit tags as read-time soft tags for fetch cache lookup: - [Next.js patch-fetch passes `softTags: implicitTags?.tags`](https://github.com/vercel/next.js/blob/6d4a405f/packages/next/src/server/lib/patch-fetch.ts#L968-L975) - [Next.js incremental cache checks `tags + softTags`](https://github.com/vercel/next.js/blob/6d4a405f/packages/next/src/server/lib/incremental-cache/index.ts#L508-L516) - [Next.js derives implicit layout and path tags](https://github.com/vercel/next.js/blob/6d4a405f/packages/next/src/server/lib/implicit-tags.ts#L25-L104) - [Next.js `revalidatePath()` normalizes paths into `_N_T_` tags](https://github.com/vercel/next.js/blob/6d4a405f/packages/next/src/server/web/spec-extension/revalidate.ts#L92-L117) - [Next.js has coverage that on-demand path revalidation refreshes fetches](https://github.com/vercel/next.js/blob/6d4a405f/test/e2e/app-dir/app-static/app-static.test.ts#L674-L704) ## Approach Add request-scoped fetch soft tags and set them from the generated App Router entry after final route matching and inside ISR regeneration scopes. Pass those soft tags to fetch cache reads. Memory and KV cache handlers now treat revalidated soft tags as read-time misses, but only stored entry tags trigger deletion. This keeps shared fetch entries from being permanently coupled to one route path while still matching the Next.js invalidation decision. ## Validation - `vp run vinext#build` - `vp test run tests/fetch-cache.test.ts tests/kv-cache-handler.test.ts tests/isr-cache.test.ts tests/entry-templates.test.ts` - `vp check` ## Risks / follow-ups This PR follows the existing vinext path-tag derivation helper. It does not broaden the scope to unrelated `revalidatePath()` parity gaps, such as root/index alias handling. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:01 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#946
No description provided.