Repository: vercel/next.js
Pull request: Update matching query and route param handling

Validated this is un-necessary and handling an edge case we don't need to be worried about and it's better to leave the query param for this case instead

Deploy tests checked https://github.com/vercel/vercel/actions/runs/16058085256/job/45317414336?pr=13522 and https://github.com/vercel/next.js/actions/runs/16059083218/job/45320691422

Changed files:
filename: packages/next/src/build/templates/app-route.ts
patch: @@ -195,6 +195,7 @@ export async function handler(
   }
   const nodeNextReq = new NodeNextRequest(req)
   const nodeNextRes = new NodeNextResponse(res)
+
   const nextReq = NextRequestAdapter.fromNodeNextRequest(
     nodeNextReq,
     signalFromNodeResponse(res)

filename: packages/next/src/server/route-modules/app-route/module.ts
patch: @@ -228,6 +228,7 @@ export class AppRouteRouteModule extends RouteModule<
     // Automatically implement some methods if they aren't implemented by the
     // userland module.
     this.methods = autoImplementMethods(userland)
+    this.isAppRouter = true
 
     // Get the non-static methods for this route.
     this.hasNonStaticMethods = hasNonStaticMethods(userland)

filename: packages/next/src/server/route-modules/route-module.ts
patch: @@ -641,25 +641,34 @@ export abstract class RouteModule<
     }
 
     const routeParamKeys = new Set<string>()
-    const combinedParamKeys = [...routeParamKeys]
-
-    for (const key of rewriteParamKeys) {
-      // We only want to filter rewrite param keys from the URL
-      // if they are matches from the URL e.g. the key/value matches
-      // before and after applying the rewrites /:path for /hello and
-      // { path: 'hello' } but not for { path: 'another' } and /hello
-      // TODO: we should prefix rewrite param keys the same as we do
-      // for dynamic routes so we can identify them properly
-      const originalValue = Array.isArray(originalQuery[key])
-        ? originalQuery[key].join('')
-        : originalQuery[key]
-
-      const queryValue = Array.isArray(query[key])
-        ? query[key].join('')
-        : query[key]
-
-      if (!(key in originalQuery) || originalValue === queryValue) {
-        combinedParamKeys.push(key)
+    const combinedParamKeys = []
+
+    // we don't include rewriteParamKeys in the combinedParamKeys
+    // for app router since the searchParams is populated from the
+    // URL so we don't want to strip the rewrite params from the URL
+    // so that searchParams can include them
+    if (!this.isAppRouter) {
+      for (const key of [
+        ...rewriteParamKeys,
+        ...Object.keys(serverUtils.defaultRouteMatches || {}),
+      ]) {
+        // We only want to filter rewrite param keys from the URL
+        // if they are matches from the URL e.g. the key/value matches
+        // before and after applying the rewrites /:path for /hello and
+        // { path: 'hello' } but not for { path: 'another' } and /hello
+        // TODO: we should prefix rewrite param keys the same as we do
+        // for dynamic routes so we can identify them properly
+        const originalValue = Array.isArray(originalQuery[key])
+          ? originalQuery[key].join('')
+          : originalQuery[key]
+
+        const queryValue = Array.isArray(query[key])
+          ? query[key].join('')
+          : query[key]
+
+        if (!(key in originalQuery) || originalValue === queryValue) {
+          combinedParamKeys.push(key)
+        }
       }
     }
 

filename: packages/next/src/server/server-utils.ts
patch: @@ -34,8 +34,7 @@ import { getSelectedParams } from '../client/components/router-reducer/compute-c
 
 function filterInternalQuery(
   query: Record<string, undefined | string | string[]>,
-  paramKeys: string[],
-  defaultRouteRegex: ReturnType<typeof getNamedRouteRegex> | undefined
+  paramKeys: string[]
 ) {
   // this is used to pass query information in rewrites
   // but should not be exposed in final query
@@ -52,8 +51,7 @@ function filterInternalQuery(
     if (
       isNextQueryPrefix ||
       isNextInterceptionMarkerPrefix ||
-      paramKeys.includes(key) ||
-      (defaultRouteRegex && Object.keys(defaultRouteRegex.groups).includes(key))
+      paramKeys.includes(key)
     ) {
       delete query[key]
     }
@@ -62,8 +60,7 @@ function filterInternalQuery(
 
 export function normalizeCdnUrl(
   req: BaseNextRequest | IncomingMessage,
-  paramKeys: string[],
-  defaultRouteRegex: ReturnType<typeof getNamedRouteRegex> | undefined
+  paramKeys: string[]
 ) {
   // make sure to normalize req.url from CDNs to strip dynamic and rewrite
   // params from the query which are added during routing
@@ -74,7 +71,7 @@ export function normalizeCdnUrl(
     return req.url
   }
   delete (_parsedUrl as any).search
-  filterInternalQuery(_parsedUrl.query, paramKeys, defaultRouteRegex)
+  filterInternalQuery(_parsedUrl.query, paramKeys)
 
   req.url = formatUrl(_parsedUrl)
 }
@@ -484,15 +481,15 @@ export function getServerUtils({
     normalizeCdnUrl: (
       req: BaseNextRequest | IncomingMessage,
       paramKeys: string[]
-    ) => normalizeCdnUrl(req, paramKeys, defaultRouteRegex),
+    ) => normalizeCdnUrl(req, paramKeys),
 
     interpolateDynamicPath: (
       pathname: string,
       params: Record<string, undefined | string | string[]>
     ) => interpolateDynamicPath(pathname, params, defaultRouteRegex),
 
     filterInternalQuery: (query: ParsedUrlQuery, paramKeys: string[]) =>
-      filterInternalQuery(query, paramKeys, defaultRouteRegex),
+      filterInternalQuery(query, paramKeys),
   }
 }
 

filename: packages/next/src/server/web-server.ts
patch: @@ -186,7 +186,7 @@ export default class NextWebServer extends BaseServer<
           normalizedParams,
           routeRegex
         )
-        normalizeCdnUrl(req, Object.keys(routeRegex.routeKeys), routeRegex)
+        normalizeCdnUrl(req, Object.keys(routeRegex.routeKeys))
       }
     }