Skip to content

Commit ce13e97

Browse files
authored
feat(next): pass full initReq context to server functions and dashboard widgets (#15427)
Currently, we only pass `importMap` and `req` to all server functions. This PR extends that interface by passing the following **additional arguments** to every server function and every dashboard widget: - `cookies` - `locale` - `permissions` This change comes at **no extra cost**, since all of these values are already available via `initReq`, which we are already calling. ## Why? I’m working on rendering the list view in a dashboard widget through a server function, which requires context such as `permissions`. Recomputing these values would be redundant and inefficient, given that they’ve already been calculated in `handleServerFunctions` and we can just pass them through. By passing them through once, we avoid duplicate work and make server functions easier to use.
1 parent de4fa0c commit ce13e97

File tree

8 files changed

+44
-44
lines changed

8 files changed

+44
-44
lines changed

packages/next/src/utilities/handleServerFunctions.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ServerFunction, ServerFunctionHandler } from 'payload'
1+
import type { DefaultServerFunctionArgs, ServerFunction, ServerFunctionHandler } from 'payload'
22

33
import { _internal_renderFieldHandler, copyDataFromLocaleHandler } from '@payloadcms/ui/rsc'
44
import { buildFormStateHandler } from '@payloadcms/ui/utilities/buildFormState'
@@ -38,24 +38,22 @@ export const handleServerFunctions: ServerFunctionHandler = async (args) => {
3838
serverFunctions: extraServerFunctions,
3939
} = args
4040

41-
const { req } = await initReq({
41+
const { cookies, locale, permissions, req } = await initReq({
4242
configPromise,
4343
importMap,
4444
key: 'RootLayout',
4545
})
4646

47-
const augmentedArgs: Parameters<ServerFunction>[0] = {
47+
const augmentedArgs: DefaultServerFunctionArgs = {
4848
...fnArgs,
49+
cookies,
4950
importMap,
51+
locale,
52+
permissions,
5053
req,
5154
}
5255

53-
const serverFunctions = {
54-
...baseServerFunctions,
55-
...(extraServerFunctions || {}),
56-
}
57-
58-
const fn = serverFunctions[fnKey]
56+
const fn = extraServerFunctions?.[fnKey] || baseServerFunctions[fnKey]
5957

6058
if (!fn) {
6159
throw new Error(`Unknown Server Function: ${fnKey}`)

packages/next/src/utilities/initReq.ts

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
import type { AcceptedLanguages, I18n, I18nClient } from '@payloadcms/translations'
2-
import type {
3-
ImportMap,
4-
Locale,
5-
Payload,
6-
PayloadRequest,
7-
SanitizedConfig,
8-
SanitizedPermissions,
9-
TypedUser,
10-
} from 'payload'
1+
import type { I18n, I18nClient } from '@payloadcms/translations'
2+
import type { ImportMap, InitReqResult, PayloadRequest, SanitizedConfig } from 'payload'
113

124
import { initI18n } from '@payloadcms/translations'
135
import { headers as getHeaders } from 'next/headers.js'
@@ -23,26 +15,14 @@ import {
2315
import { getRequestLocale } from './getRequestLocale.js'
2416
import { selectiveCache } from './selectiveCache.js'
2517

26-
type Result = {
27-
cookies: Map<string, string>
28-
headers: Awaited<ReturnType<typeof getHeaders>>
29-
languageCode: AcceptedLanguages
30-
locale?: Locale
31-
permissions: SanitizedPermissions
32-
req: PayloadRequest
33-
}
34-
3518
type PartialResult = {
3619
i18n: I18nClient
37-
languageCode: AcceptedLanguages
38-
payload: Payload
39-
responseHeaders: Headers
40-
user: null | TypedUser
41-
}
20+
} & Pick<InitReqResult, 'languageCode'> &
21+
Pick<PayloadRequest, 'payload' | 'responseHeaders' | 'user'>
4222

4323
// Create cache instances for different parts of our application
4424
const partialReqCache = selectiveCache<PartialResult>('partialReq')
45-
const reqCache = selectiveCache<Result>('req')
25+
const reqCache = selectiveCache<InitReqResult>('req')
4626

4727
/**
4828
* Initializes a full request object, including the `req` object and access control.
@@ -60,7 +40,7 @@ export const initReq = async function ({
6040
importMap: ImportMap
6141
key: string
6242
overrides?: Parameters<typeof createLocalReq>[0]
63-
}): Promise<Result> {
43+
}): Promise<InitReqResult> {
6444
const headers = await getHeaders()
6545
const cookies = parseCookies(headers)
6646

packages/next/src/views/Dashboard/Default/ModularDashboard/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export async function ModularDashboard(props: DashboardViewServerProps) {
2626
const { defaultLayout = [], widgets = [] } = props.payload.config.admin.dashboard || {}
2727
const { importMap } = props.payload
2828
const { user } = props
29-
const { req } = props.initPageResult
29+
const { cookies, locale, permissions, req } = props.initPageResult
3030
const { i18n } = req
3131

3232
const layout =
@@ -40,6 +40,9 @@ export async function ModularDashboard(props: DashboardViewServerProps) {
4040
Component: widgets.find((widget) => widget.slug === widgetSlug)?.ComponentPath,
4141
importMap,
4242
serverProps: {
43+
cookies,
44+
locale,
45+
permissions,
4346
req,
4447
widgetSlug,
4548
// TODO: widgets will support state in the future

packages/next/src/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export type GetDefaultLayoutServerFnReturnType = {
2323
export const getDefaultLayoutHandler: ServerFunction<
2424
GetDefaultLayoutServerFnArgs,
2525
Promise<GetDefaultLayoutServerFnReturnType>
26-
> = async ({ req }) => {
26+
> = async ({ cookies, locale, permissions, req }) => {
2727
if (!req.user) {
2828
throw new Error('Unauthorized')
2929
}
@@ -40,6 +40,9 @@ export const getDefaultLayoutHandler: ServerFunction<
4040
Component: widgets.find((widget) => widget.slug === widgetSlug)?.ComponentPath,
4141
importMap,
4242
serverProps: {
43+
cookies,
44+
locale,
45+
permissions,
4346
req,
4447
widgetSlug,
4548
} satisfies WidgetServerProps,

packages/next/src/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export type RenderWidgetServerFnReturnType = {
2626
export const renderWidgetHandler: ServerFunction<
2727
RenderWidgetServerFnArgs,
2828
RenderWidgetServerFnReturnType
29-
> = ({ req, /* widgetData, */ widgetSlug }) => {
29+
> = ({ cookies, locale, permissions, req, widgetSlug }) => {
3030
if (!req.user) {
3131
throw new Error('Unauthorized')
3232
}
@@ -61,6 +61,9 @@ export const renderWidgetHandler: ServerFunction<
6161
const serverProps: WidgetServerProps = {
6262
req,
6363
// TODO: widgetData: widgetData || {},
64+
cookies,
65+
locale,
66+
permissions,
6467
widgetSlug,
6568
}
6669

packages/payload/src/admin/functions/index.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import type { AcceptedLanguages } from '@payloadcms/translations'
2+
13
import type { ImportMap } from '../../bin/generateImportMap/index.js'
2-
import type { SanitizedConfig } from '../../config/types.js'
4+
import type { Locale, SanitizedConfig } from '../../config/types.js'
35
import type { PaginatedDocs } from '../../database/types.js'
46
import type { Slugify } from '../../fields/baseFields/slug/index.js'
57
import type {
@@ -8,15 +10,26 @@ import type {
810
FieldPaths,
911
FolderSortKeys,
1012
GlobalSlug,
13+
SanitizedPermissions,
1114
} from '../../index.js'
1215
import type { PayloadRequest, Sort, Where } from '../../types/index.js'
1316
import type { ColumnsFromURL } from '../../utilities/transformColumnPreferences.js'
1417

15-
export type DefaultServerFunctionArgs = {
16-
importMap: ImportMap
18+
export type InitReqResult = {
19+
cookies: Map<string, string>
20+
// TODO: Remove in 4.0. Duplicative, already available in req.headers
21+
headers: Headers
22+
// TODO: Remove in 4.0. Duplicative, already available in req.i18n.language
23+
languageCode: AcceptedLanguages
24+
locale?: Locale
25+
permissions: SanitizedPermissions
1726
req: PayloadRequest
1827
}
1928

29+
export type DefaultServerFunctionArgs = {
30+
importMap: ImportMap
31+
} & Pick<InitReqResult, 'cookies' | 'locale' | 'permissions' | 'req'>
32+
2033
export type ServerFunctionArgs = {
2134
args: Record<string, unknown>
2235
name: string

packages/payload/src/admin/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ export type {
585585
BuildTableStateArgs,
586586
DefaultServerFunctionArgs,
587587
GetFolderResultsComponentAndDataArgs,
588+
InitReqResult,
588589
ListQuery,
589590
ServerFunction,
590591
ServerFunctionArgs,
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import type { PayloadRequest } from '../../index.js'
1+
import type { InitReqResult } from '../../index.js'
22

33
export enum EntityType {
44
collection = 'collections',
55
global = 'globals',
66
}
77

88
export type WidgetServerProps = {
9-
req: PayloadRequest
109
widgetData?: Record<string, unknown>
1110
widgetSlug: string
12-
}
11+
} & Pick<InitReqResult, 'cookies' | 'locale' | 'permissions' | 'req'>

0 commit comments

Comments
 (0)