Skip to content

Commit fec7ca6

Browse files
bgublubieowoce
authored andcommitted
feat: handle rootParams in routes (finish changes) (#81086)
This PR enables the use of root params in route handlers by: 1. Modifying the app route module to extract root params from context.params based on rootParamNames 2. Passing these root params to the request store in route handlers 3. Removing the error that previously prevented using root params in route handlers 4. Simplifying the rootParams handling by directly using string arrays instead of ParamInfo objects 5. Replacing the custom encodeToBase64 utility with standard Buffer.from().toString('base64')
1 parent 71be2fd commit fec7ca6

File tree

10 files changed

+68
-31
lines changed

10 files changed

+68
-31
lines changed

crates/next-api/src/app.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use next_core::{
44
app_segment_config::NextSegmentConfig,
55
app_structure::{
66
AppPageLoaderTree, CollectedRootParams, Entrypoint as AppEntrypoint,
7-
Entrypoints as AppEntrypoints, FileSystemPathVec, MetadataItem, collect_root_params,
8-
get_entrypoints,
7+
Entrypoints as AppEntrypoints, FileSystemPathVec, MetadataItem, RootParamVecOption,
8+
collect_root_params, get_entrypoints,
99
},
1010
get_edge_resolve_options_context, get_next_package,
1111
next_app::{
@@ -992,7 +992,10 @@ pub fn app_entry_point_to_route(
992992
) -> Vc<Route> {
993993
match entrypoint {
994994
AppEntrypoint::AppPage {
995-
pages, loader_tree, ..
995+
pages,
996+
loader_tree,
997+
root_params,
998+
..
996999
} => Route::AppPage(
9971000
pages
9981001
.into_iter()
@@ -1006,6 +1009,7 @@ pub fn app_entry_point_to_route(
10061009
},
10071010
app_project,
10081011
page: page.clone(),
1012+
root_params,
10091013
}
10101014
.resolved_cell(),
10111015
),
@@ -1017,6 +1021,7 @@ pub fn app_entry_point_to_route(
10171021
},
10181022
app_project,
10191023
page,
1024+
root_params,
10201025
}
10211026
.resolved_cell(),
10221027
),
@@ -1027,6 +1032,7 @@ pub fn app_entry_point_to_route(
10271032
page,
10281033
path,
10291034
root_layouts,
1035+
root_params,
10301036
..
10311037
} => Route::AppRoute {
10321038
original_name: page.to_string().into(),
@@ -1035,17 +1041,24 @@ pub fn app_entry_point_to_route(
10351041
ty: AppEndpointType::Route { path, root_layouts },
10361042
app_project,
10371043
page,
1044+
root_params,
10381045
}
10391046
.resolved_cell(),
10401047
),
10411048
},
1042-
AppEntrypoint::AppMetadata { page, metadata, .. } => Route::AppRoute {
1049+
AppEntrypoint::AppMetadata {
1050+
page,
1051+
metadata,
1052+
root_params,
1053+
..
1054+
} => Route::AppRoute {
10431055
original_name: page.to_string().into(),
10441056
endpoint: ResolvedVc::upcast(
10451057
AppEndpoint {
10461058
ty: AppEndpointType::Metadata { metadata },
10471059
app_project,
10481060
page,
1061+
root_params,
10491062
}
10501063
.resolved_cell(),
10511064
),
@@ -1083,6 +1096,7 @@ struct AppEndpoint {
10831096
ty: AppEndpointType,
10841097
app_project: ResolvedVc<AppProject>,
10851098
page: AppPage,
1099+
root_params: ResolvedVc<RootParamVecOption>,
10861100
}
10871101

10881102
#[turbo_tasks::value_impl]
@@ -1129,6 +1143,7 @@ impl AppEndpoint {
11291143
self.app_project.project().project_path().owned().await?,
11301144
config,
11311145
next_config,
1146+
*self.root_params,
11321147
))
11331148
}
11341149

crates/next-core/src/next_app/app_route_entry.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub async fn get_app_route_entry(
3535
project_root: FileSystemPath,
3636
original_segment_config: Option<Vc<NextSegmentConfig>>,
3737
next_config: Vc<NextConfig>,
38+
root_params: Vc<crate::app_structure::RootParamVecOption>,
3839
) -> Result<Vc<AppEntry>> {
3940
let segment_from_source = parse_segment_config_from_source(source);
4041
let config = if let Some(original_segment_config) = original_segment_config {
@@ -70,6 +71,18 @@ pub async fn get_app_route_entry(
7071
.map(RcStr::from)
7172
.unwrap_or_else(|| "\"\"".into());
7273

74+
// Convert root params to JSON array of parameter names
75+
let root_param_names = if let Some(root_params_vec) = &*root_params.await? {
76+
serde_json::to_string(
77+
&root_params_vec
78+
.iter()
79+
.map(|param| param.param.as_str())
80+
.collect::<Vec<_>>(),
81+
)?
82+
} else {
83+
"[]".to_string()
84+
};
85+
7386
// Load the file from the next.js codebase.
7487
let virtual_source = load_next_js_template(
7588
"app-route.js",
@@ -85,7 +98,7 @@ pub async fn get_app_route_entry(
8598
},
8699
fxindexmap! {
87100
"nextConfigOutput" => output_type,
88-
// "rootParamNames" => ... // TODO(root-params)
101+
"rootParamNames" => root_param_names.into(),
89102
},
90103
fxindexmap! {},
91104
)

crates/next-core/src/next_app/metadata/route.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub async fn get_app_metadata_route_entry(
103103
project_root,
104104
Some(segment_config),
105105
next_config,
106+
Vc::cell(None), // Metadata routes don't have root params
106107
))
107108
}
108109

packages/next/src/build/entries.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,10 @@ export async function createEntrypoints(
704704
page,
705705
name: serverBundlePath,
706706
pagePath: absolutePagePath,
707-
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
707+
rootParams:
708+
(staticInfo as AppPageStaticInfo).rootParams?.map(
709+
(p) => p.param
710+
) || [],
708711
appDir,
709712
appPaths: matchedAppPaths,
710713
pageExtensions,
@@ -783,7 +786,10 @@ export async function createEntrypoints(
783786
name: serverBundlePath,
784787
page,
785788
pagePath: absolutePagePath,
786-
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
789+
rootParams:
790+
(staticInfo as AppPageStaticInfo).rootParams?.map(
791+
(p) => p.param
792+
) || [],
787793
appDir: appDir!,
788794
appPaths: matchedAppPaths,
789795
pageExtensions,

packages/next/src/build/webpack/loaders/next-app-loader/create-app-route-code.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { AppPathnameNormalizer } from '../../../../server/normalizers/built/app/
1111
import { loadEntrypoint } from '../../../load-entrypoint'
1212
import type { PageExtensions } from '../../../page-extensions-type'
1313
import { getFilenameAndExtension } from '../next-metadata-route-loader'
14-
import type { ParamInfo } from '../next-root-params-loader'
1514

1615
export async function createAppRouteCode({
1716
appDir,
@@ -27,7 +26,7 @@ export async function createAppRouteCode({
2726
name: string
2827
page: string
2928
pagePath: string
30-
rootParams: ParamInfo[]
29+
rootParams: string[]
3130
resolveAppRoute: (
3231
pathname: string
3332
) => Promise<string | undefined> | string | undefined
@@ -81,7 +80,7 @@ export async function createAppRouteCode({
8180
},
8281
{
8382
nextConfigOutput: JSON.stringify(nextConfigOutput),
84-
rootParamNames: JSON.stringify(rootParams.map((p) => p.param)),
83+
rootParamNames: JSON.stringify(rootParams),
8584
}
8685
)
8786
}

packages/next/src/build/webpack/loaders/next-app-loader/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,12 @@ import type { PageExtensions } from '../../../page-extensions-type'
3333
import { PARALLEL_ROUTE_DEFAULT_PATH } from '../../../../client/components/builtin/default'
3434
import type { Compilation } from 'webpack'
3535
import { createAppRouteCode } from './create-app-route-code'
36-
import type { ParamInfo } from '../next-root-params-loader'
3736

3837
export type AppLoaderOptions = {
3938
name: string
4039
page: string
4140
pagePath: string
42-
rootParams: ParamInfo[]
41+
rootParams: string[]
4342
appDir: string
4443
appPaths: readonly string[] | null
4544
preferredRegion: string | string[] | undefined

packages/next/src/server/dev/hot-reloader-webpack.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,10 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
970970
entryData.absolutePagePath
971971
).replace(/\\/g, '/')
972972
),
973-
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
973+
rootParams:
974+
(staticInfo as AppPageStaticInfo).rootParams?.map(
975+
(p) => p.param
976+
) || [],
974977
appDir: this.appDir!,
975978
pageExtensions: this.config.pageExtensions,
976979
rootDir: this.dir,
@@ -1094,7 +1097,10 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
10941097
page,
10951098
appPaths: entryData.appPaths,
10961099
pagePath,
1097-
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
1100+
rootParams:
1101+
(staticInfo as AppPageStaticInfo).rootParams?.map(
1102+
(p) => p.param
1103+
) || [],
10981104
appDir: this.appDir!,
10991105
pageExtensions: this.config.pageExtensions,
11001106
rootDir: this.dir,

packages/next/src/server/request/root-params.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,6 @@ export async function getRootParam(paramName: string): Promise<ParamValue> {
207207

208208
const actionStore = actionAsyncStorage.getStore()
209209
if (actionStore) {
210-
if (actionStore.isAppRoute) {
211-
// TODO(root-params): add support for route handlers
212-
throw new Error(
213-
`Route ${workStore.route} used ${apiName} inside a Route Handler. Support for this API in Route Handlers is planned for a future version of Next.js.`
214-
)
215-
}
216210
if (actionStore.isAction) {
217211
// Actions are not fundamentally tied to a route (even if they're always submitted from some page),
218212
// so root params would be inconsistent if an action is called from multiple roots.

packages/next/src/server/route-modules/app-route/module.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,7 @@ export class AppRouteRouteModule extends RouteModule<
388388
(prerenderStore = {
389389
type: 'prerender',
390390
phase: 'action',
391-
// This replicates prior behavior where rootParams is empty in routes
392-
// TODO we need to make this have the proper rootParams for this route
393-
rootParams: {},
391+
rootParams: requestStore.rootParams,
394392
implicitTags,
395393
renderSignal: prospectiveController.signal,
396394
controller: prospectiveController,
@@ -484,7 +482,7 @@ export class AppRouteRouteModule extends RouteModule<
484482
const finalRoutePrerenderStore: PrerenderStore = (prerenderStore = {
485483
type: 'prerender',
486484
phase: 'action',
487-
rootParams: {},
485+
rootParams: requestStore.rootParams,
488486
implicitTags,
489487
renderSignal: finalController.signal,
490488
controller: finalController,
@@ -569,7 +567,7 @@ export class AppRouteRouteModule extends RouteModule<
569567
prerenderStore = {
570568
type: 'prerender-legacy',
571569
phase: 'action',
572-
rootParams: {},
570+
rootParams: requestStore.rootParams,
573571
implicitTags,
574572
revalidate: defaultRevalidate,
575573
expire: INFINITE_CACHE,
@@ -694,10 +692,20 @@ export class AppRouteRouteModule extends RouteModule<
694692
null
695693
)
696694

695+
// Extract root params from context.params based on rootParamNames
696+
const rootParams: Record<string, string | string[] | undefined> = {}
697+
if (context.params && this.rootParamNames.length > 0) {
698+
for (const paramName of this.rootParamNames) {
699+
if (paramName in context.params) {
700+
rootParams[paramName] = context.params[paramName]
701+
}
702+
}
703+
}
704+
697705
const requestStore = createRequestStoreForAPI(
698706
req,
699707
req.nextUrl,
700-
{}, // TODO: real rootParams
708+
rootParams,
701709
implicitTags,
702710
undefined,
703711
context.prerenderManifest.preview
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { lang, locale } from 'next/root-params'
22

33
export async function GET() {
4-
return Response.json(
5-
// TODO(root-params): We're missing some wiring to set `requestStore.rootParams`,
6-
// so both of these will currently return undefined
7-
{ lang: await lang(), locale: await locale() }
8-
)
4+
return Response.json({ lang: await lang(), locale: await locale() })
95
}

0 commit comments

Comments
 (0)