mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #610] [CLOSED] fix: detect dynamic API usage in route handlers to prevent stale ISR cache hits #710
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#710
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/610
Author: @southpolesteve
Created: 3/20/2026
Status: ❌ Closed
Base:
main← Head:fix/route-handler-dynamic-detection📝 Commits (10+)
efcda09fix: detect dynamic API usage in route handlers to prevent stale ISR cache hits057bcdafix: wrap synthetic request in proxy during background regeneration30b6096fix: remove TypeScript syntax from generated JavaScript entrydfd4425fix: bind proxied Request methods to original target26b52e5fix: clear dynamic usage flag before handler execution72ab864fix: clear dynamic usage in background regen path toofff3b10refactor: extract ISR cache logic into wrapper and use counter-based dynamic detection39a83d0fix: update unified-request-context tests for dynamicUsageCount rename34aac05fix: reset dynamic counter before handler and fix remaining test assertionsb0302cffix: use consumeDynamicUsage() instead of counter for handler detection📊 Changes
7 files changed (+1523 additions, -911 deletions)
View changed files
📝
packages/vinext/src/build/prerender.ts(+1 -1)📝
packages/vinext/src/entries/app-rsc-entry.ts(+210 -125)📝
packages/vinext/src/shims/headers.ts(+16 -8)📝
packages/vinext/src/shims/unified-request-context.ts(+1 -1)📝
tests/__snapshots__/entry-templates.test.ts.snap(+1260 -750)📝
tests/app-router.test.ts(+17 -8)📝
tests/unified-request-context.test.ts(+18 -18)📄 Description
Summary
Route handlers that access
request.headersorrequest.nextUrl.searchParamsare dynamic and should not be served from the ISR cache. This PR adds proper dynamic detection matching Next.js behavior, replacing the query-string-in-cache-key approach from PR #603.How it works
Three new mechanisms:
__proxyRouteRequest: Wraps theRequestpassed to route handlers in a Proxy. Accessing.headersor.nextUrl.searchParamscallsmarkDynamicUsage(), which the existingconsumeDynamicUsage()check on the write path already respects.__dynamicRouteHandlers: A module-levelSet<string>that remembers route patterns whose handlers used dynamic APIs. On the first request (cache MISS), the handler runs and dynamic usage is detected. On subsequent requests, the cache read path skips the cache for known-dynamic handlers.Cache read guard: The ISR cache read condition now includes
!__dynamicRouteHandlers.has(handler.pattern), preventing stale cache hits for dynamic handlers.Why not query-string-in-cache-key?
PR #603 took a different approach: including the query string in the ISR cache key. James pointed out that this diverges from Next.js, which uses pathname-only cache keys (source). In Next.js, handlers that read searchParams are detected as dynamic and never cached at all.
This PR matches that behavior: dynamic handlers are never cached, static handlers use pathname-only keys.
Granularity
The Proxy is granular about what triggers dynamic detection:
request.methodrequest.url(string)request.nextUrl.pathnamerequest.nextUrl.searchParamsrequest.headersThe one gap (same as Next.js):
new URL(request.url).searchParamsbypasses the Proxy since it operates on the raw URL string. Developers should userequest.nextUrl.searchParamsinstead.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.