fix(vercel): handle ISR requests with passQuery: true#3539
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
passQuery: truepassQuery: true
|
hey @pi0, do you plan to release the fix in upcoming version maybe? |
|
Would love to see this merged. Using works but is not ideal. |
|
The problem I face with https://pkg.pr.new/nitrojs/nitro/nitropack@3539: Building and starting the application with node-presets causes an issue with the public folder resolution in .output results in errors like this: There is no public folder in .output/server/chunks |
📝 WalkthroughWalkthroughThe PR introduces a new constant Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/presets/vercel/runtime/vercel.ts (2)
15-23: Variable naming:isrRouteis misleading.The variable
isrRouteactually holds thex-now-route-matchesheader value (a query string), not a route. Consider renaming torouteMatchesHeaderorisrHeaderfor clarity.- const isrRoute = req.headers["x-now-route-matches"] as string; - if (isrRoute) { - const { [ISR_URL_PARAM]: url } = parseQuery(isrRoute); + const routeMatchesHeader = req.headers["x-now-route-matches"] as string; + if (routeMatchesHeader) { + const { [ISR_URL_PARAM]: url } = parseQuery(routeMatchesHeader);
17-22: Consider extracting duplicate ISR validation logic.Lines 17-22 and 32-39 share nearly identical validation logic: extract URL from ISR parameter, check if it's a string, get route rules, verify ISR is enabled, then update
req.url. This duplication increases maintenance burden.Consider extracting a helper function:
function trySetIsrUrl( req: any, url: unknown, preserveParams?: Record<string, any> ): void { if (url && typeof url === "string") { const routeRules = getRouteRulesForPath(url) as NitroRouteRules; if (routeRules.isr) { req.url = preserveParams ? withQuery(url, preserveParams) : url; } } }Then simplify the main logic:
const routeMatchesHeader = req.headers["x-now-route-matches"] as string; if (routeMatchesHeader) { const { [ISR_URL_PARAM]: url } = parseQuery(routeMatchesHeader); trySetIsrUrl(req, url); } else { const queryIndex = req.url!.indexOf("?"); const urlQueryIndex = queryIndex === -1 ? -1 : req.url!.indexOf(`${ISR_URL_PARAM}=`, queryIndex); if (urlQueryIndex !== -1) { const { [ISR_URL_PARAM]: url, ...params } = parseQuery( req.url!.slice(queryIndex) ); trySetIsrUrl(req, url, params); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
src/presets/vercel/runtime/consts.ts(1 hunks)src/presets/vercel/runtime/vercel.ts(1 hunks)src/presets/vercel/utils.ts(4 hunks)test/presets/vercel.test.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/presets/vercel/utils.ts (1)
src/presets/vercel/runtime/consts.ts (1)
ISR_URL_PARAM(1-1)
src/presets/vercel/runtime/vercel.ts (3)
src/runtime/internal/app.ts (1)
nitroApp(220-220)src/presets/netlify/legacy/runtime/netlify.ts (1)
handler(7-28)src/presets/vercel/runtime/consts.ts (1)
ISR_URL_PARAM(1-1)
🔇 Additional comments (10)
src/presets/vercel/runtime/consts.ts (1)
1-1: LGTM! Centralized constant for ISR parameter.The constant effectively standardizes the ISR URL parameter across the codebase, replacing hardcoded strings and improving maintainability.
src/presets/vercel/utils.ts (4)
14-14: LGTM! Import of centralized ISR parameter constant.
237-239: LGTM! Consistent use of ISR_URL_PARAM in route configuration.The named capture group and destination query string correctly reference the centralized constant.
246-264: LGTM! ISR route handling uses centralized constant.The routing logic properly applies ISR_URL_PARAM across both source patterns and destination URLs for ISR-enabled and ISR-disabled routes.
475-480: Guard against undefinedallowQuerybefore calling.includes().The code checks if
prerenderConfig.allowQueryexists before calling.includes(), but it doesn't guard againstallowQuerybeing undefined when pushing the ISR parameter. IfallowQueryisundefined, line 477 will throw a TypeError.Apply this diff to properly initialize or guard the array:
- if ( - prerenderConfig.allowQuery && - !prerenderConfig.allowQuery.includes(ISR_URL_PARAM) - ) { - prerenderConfig.allowQuery.push(ISR_URL_PARAM); - } + if (prerenderConfig.allowQuery) { + if (!prerenderConfig.allowQuery.includes(ISR_URL_PARAM)) { + prerenderConfig.allowQuery.push(ISR_URL_PARAM); + } + } else if (prerenderConfig.passQuery) { + prerenderConfig.allowQuery = [ISR_URL_PARAM]; + }Alternatively, if
allowQueryshould always include ISR_URL_PARAM when ISR is configured:- if ( - prerenderConfig.allowQuery && - !prerenderConfig.allowQuery.includes(ISR_URL_PARAM) - ) { - prerenderConfig.allowQuery.push(ISR_URL_PARAM); - } + if (!prerenderConfig.allowQuery) { + prerenderConfig.allowQuery = []; + } + if (!prerenderConfig.allowQuery.includes(ISR_URL_PARAM)) { + prerenderConfig.allowQuery.push(ISR_URL_PARAM); + }Likely an incorrect or invalid review comment.
test/presets/vercel.test.ts (2)
116-150: LGTM! Test fixtures updated to reflect new ISR parameter.The test expectations correctly verify that ISR routes use
__isr_routein both source capture groups and destination query strings, aligning with the centralized constant.
463-463: LGTM! Prerender config expectation updated.The test correctly verifies that
__isr_routeis included in theallowQueryarray alongside user-facing query parameters.src/presets/vercel/runtime/vercel.ts (3)
3-8: LGTM! Necessary imports for ISR-aware routing.The imports correctly bring in route rules lookup, query manipulation utilities, and the centralized ISR parameter constant.
38-38: LGTM! Preserves non-ISR query parameters.The use of
withQuery(url, params)correctly preserves user-facing query parameters when reconstructing the URL for the passQuery flow, which addresses the issue described in #1880.
26-33: The non-null assertions are safe but redundant.In Node.js HTTP request handlers,
req.urlis always defined by the HTTP specification and guaranteed to be set when the request object is created. The h3 framework maintains this contract. The non-null assertions on lines 26, 30, and 33 are technically unnecessary sincereq.urlwill always have a value in this context. Consider removing the!operators for cleaner code, though the current implementation is functionally correct.
|
@fmoessle I couldn't reproduce error on your branch... This PR is also only affecting vercel preset not sure how can it break node-server. If you continue to having them on nightly or next release please feel free to raise an issue with more steps for reproduction. |
passQuery: truepassQuery: true
As this branch was now merged and deleted and as far as I can see there is no new release in nitro v2. What's the proposed way now? Should it now target the commit? |
|
You can use nightly channel: https://nitro.build/guide/nightly |
Resolves #1880, #3594, #3651
Normally, with ISR matched routes, there is an (undocumented/legacy)
x-now-route-matchesheader that contains the full pathname of the matched route. And Nitro vercel entry uses this header value to restore the original URL, but whenpassQueryisr route rule config is set, this header won't exist anymore, and the path is invalid (isr function name). We cannot always assumeurlquery param is for ISR functions (it can be an actual query in normal routes!)This PR changes the internal
urlquery param to__isr_routeand, if it exists, also attempts to rewrite without relying on legacy header but instead using a runtime protection check to make sure the requested route matches ISR route patterns.We also make sure internal
"__isr_route"is added topassQuery(if specified)Beta testing: use nightly channel