🐞 fix(nextjs): Fix Next.js server page context/state handoff for query-parameters#339
Conversation
| if ((await options.shouldHandleRequest?.(context)) === false) { | ||
| return response | ||
| } | ||
| function applyNextjsOptimizationRequestContext( |
There was a problem hiding this comment.
When chained after another middleware that already used NextResponse.next({ request: { headers } }) to inject rewritten headers, clearForwardedRequestHeaders() deletes those overrides then rebuilds from the raw incoming request only. Any headers injected by an upstream middleware are silently dropped. The tests only verify preservation of ordinary response headers, not pre-existing request header overrides
There was a problem hiding this comment.
Dammit, I changed this several times to keep that from happening.
There was a problem hiding this comment.
Fixed, and updated the AGENTS file to make sure that functionality doesn't get wiped in another code-reduction pass.
d4b6ad3 to
a416246
Compare
| const page = mergePageContext( | ||
| mergePageContext(requestPage ?? forwardedPage, getExplicitPage(explicitPage)), | ||
| eventContext?.page, | ||
| ) |
There was a problem hiding this comment.
Currently, eventContext.page takes precedence over the explicit top-level page; is that intended?
There was a problem hiding this comment.
It seems this was a bit wrong, and I've also updated headers, cookies, and user agent handling to ensure precedence fits what one would reasonably expect.
…y-parameters ### Summary This fixes server-side audience matching for rules that depend on URL query parameters. The supported Next.js server optimization path can now populate `context.page` from the current request URL, including query parameters, so a request such as `/?test=true` can evaluate query-based audience rules instead of silently rendering baseline content. While investigating the bug, the issue turned out to span more than URL parsing. Next.js App Router splits request handling, Server Component rendering, and browser startup across different boundaries. The previous request-handler API mixed request context capture, `page()` execution, consent resolution, and cookie persistence in middleware/proxy code, which made it hard for Server Components to reliably receive page URL context without duplicating work. This PR separates those responsibilities and documents the supported integration paths. ### What changed - Added shared page-context creation in Core so request URLs are normalized into SDK-compatible `page.path`, `page.search`, `page.query`, `page.referrer`, and `page.url`. - Updated the Next.js server helpers to derive page context from: - direct request objects, - sanitized proxy/middleware-forwarded request URL headers, - explicit App Router route state via `createNextjsPageContext()`. - Replaced the page-producing Next.js request handler with `createNextjsOptimizationContextHandler()`, which only forwards sanitized request context for Server Components. - Added `@contentful/optimization-nextjs/esr` for edge/request-rendered flows that own both the incoming `Request` and outgoing `Response`, including anonymous ID persistence. - Added explicit server-to-browser Optimization state handoff through: - `serverOptimizationState` on React Web provider/root APIs, - `NextjsOptimizationState` for shared App Router layouts where page data is produced below the provider. - Introduced internal first-party `bridge-support` entry points for controlled state hydration and preview-panel access, replacing public symbol/register-preview wiring. - Added diagnostics for conflicting explicit page context versus page event properties. - Updated Next.js SSR and hybrid reference implementations to use the new request-context and state-handoff model. - Updated package READMEs, Next.js integration guides, and related concepts for consent, profile synchronization, locale handling, and server/stateless interaction tracking. ### Why the scope expanded A narrow fix that only parsed query strings would not have made the supported Next.js App Router flow reliable. Server Components do not naturally receive the full request URL, and the old middleware helper tried to solve that by performing SDK work at the request layer. That created unclear ownership over consent, page events, cookie persistence, and browser handoff. This PR makes the boundaries explicit: - proxy/middleware captures request URL context only; - Server Components own `getNextjsServerOptimizationData()` and the initial server `page()` call; - ESR helpers own request/response-based rendering flows; - React/Next client code receives server-returned Optimization data through an explicit handoff API; - preview and state hydration use internal bridge support instead of consumer-visible Core internals. ### Test coverage added or updated - Core stateless page-event tests cover forwarding request page query context. - EventBuilder tests cover `context.page` construction and explicit page-context precedence. - Core bridge tests cover first-party bridge access and Optimization data hydration. - Next.js server tests cover forwarded request URL headers, explicit page options, App Router `searchParams`, and duplicate query value normalization. - Next.js request-handler tests cover sanitized context forwarding and existing-response preservation. - Next.js ESR tests cover plain `Request`, `NextRequest`, cookie reads, cookie persistence, and persistence cleanup. - React Web provider tests cover server Optimization state being applied before `onStatesReady` and before children render. - Next.js client tests cover `NextjsOptimizationState` hydration behavior. [[NT-3568](https://contentful.atlassian.net/browse/NT-3568)]
a416246 to
6132c6e
Compare
Lotfi Anwar L Arif (Lotfi-Arif)
left a comment
There was a problem hiding this comment.
Looks great and consistent! ✅
Summary
This fixes server-side audience matching for rules that depend on URL query parameters. The supported Next.js server optimization path can now populate
context.pagefrom the current request URL, including query parameters, so a request such as/?test=truecan evaluate query-based audience rules instead of silently rendering baseline content.While investigating the bug, the issue turned out to span more than URL parsing. Next.js App Router splits request handling, Server Component rendering, and browser startup across different boundaries. The previous request-handler API mixed request context capture,
page()execution, consent resolution, and cookie persistence in middleware/proxy code, which made it hard for Server Components to reliably receive page URL context without duplicating work. This PR separates those responsibilities and documents the supported integration paths.What changed
page.path,page.search,page.query,page.referrer, andpage.url.createNextjsPageContext().createNextjsOptimizationContextHandler(), which only forwards sanitized request context for Server Components.@contentful/optimization-nextjs/esrfor edge/request-rendered flows that own both the incomingRequestand outgoingResponse, including anonymous ID persistence.serverOptimizationStateon React Web provider/root APIs,NextjsOptimizationStatefor shared App Router layouts where page data is produced below the provider.bridge-supportentry points for controlled state hydration and preview-panel access, replacing public symbol/register-preview wiring.Why the scope expanded
A narrow fix that only parsed query strings would not have made the supported Next.js App Router flow reliable. Server Components do not naturally receive the full request URL, and the old middleware helper tried to solve that by performing SDK work at the request layer. That created unclear ownership over consent, page events, cookie persistence, and browser handoff.
This PR makes the boundaries explicit:
getNextjsServerOptimizationData()and the initial serverpage()call;Test coverage added or updated
context.pageconstruction and explicit page-context precedence.searchParams, and duplicate query value normalization.Request,NextRequest, cookie reads, cookie persistence, and persistence cleanup.onStatesReadyand before children render.NextjsOptimizationStatehydration behavior.[NT-3568]