feat(next): add config.admin.serverFunctions registry for plugins#16499
Draft
tsemachh wants to merge 1 commit into
Draft
feat(next): add config.admin.serverFunctions registry for plugins#16499tsemachh wants to merge 1 commit into
tsemachh wants to merge 1 commit into
Conversation
…gins
Adds an optional Record<string, ServerFunction> on Config['admin'] so
plugins can register custom server functions through payload.config.ts
instead of asking every integrator to wire them into the serverFunctions
prop on <RootLayout /> in (payload)/layout.tsx.
handleServerFunctions resolves fnKey with this lookup order:
1. integrator-supplied serverFunctions prop on <RootLayout />
(highest priority — preserves back-compat)
2. config.admin.serverFunctions (the new registry)
3. built-in baseServerFunctions
Sanitize-time validation:
- Defaults admin.serverFunctions to {}.
- Rejects empty-string keys and non-function values with
InvalidConfiguration so misuse fails fast at config build time.
Tests:
- packages/payload/src/config/sanitize.serverFunctions.spec.ts covers
defaulting, preservation of user-supplied entries, and rejection of
invalid keys/values. The lookup-order behavior in handleServerFunctions
is exercised by the existing test/server-functions e2e suite.
100% additive and backward-compatible: integrator-supplied serverFunctions
continue to win on key conflicts; built-ins still resolve when nothing
else is registered.
Closes payloadcms#16497
Co-authored-by: Junie <junie@jetbrains.com>
config.admin.serverFunctions registry for plugins
This was referenced May 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #16497
Summary
Adds an optional
serverFunctions?: Record<string, ServerFunction>field onConfig['admin']so that plugins can register custom server functions throughpayload.config.tsinstead of asking every integrator to wire them into theserverFunctionsprop of<RootLayout />in(payload)/layout.tsx.Motivation
Today, the only way a plugin can ship a server function is to ask the integrator to:
app/(payload)/layout.tsx.serverFunctions={{ ... }}prop on<RootLayout />.This is hostile to "single-line install" plugin UX — the plugin's
payload.config.tsfactory can wire collections, globals, fields, components, endpoints, and admin routes, but it cannot wire a server function. A concrete consumer is a planned@shefing/review-buttonplugin (companion: #16496) that needs a'render-review-diff'server function on the server side.Changes
packages/payload/src/config/types.tsserverFunctions?: Record<string, ServerFunction>to theadminblock ofConfig, with a JSDoc that documents the lookup order and recommends namespaced keys (e.g.'@my-plugin/foo').SanitizedConfig.admin.serverFunctionsis automatically inherited viaDeepRequired<Config['admin']>.packages/payload/src/config/defaults.tsadmin.serverFunctionsto{}in both the staticdefaultsobject andaddDefaultsToConfigso callers can always doconfig.admin.serverFunctions[key]without a guard.packages/payload/src/config/sanitize.tssanitizeAdminConfig, validate keys are non-empty strings and values are functions. ThrowInvalidConfigurationon misuse so plugin/integrator wiring errors fail fast at config build time.packages/next/src/utilities/handleServerFunctions.tsserverFunctionsprop continues to win on key conflicts, preserving back-compat.packages/payload/src/config/sanitize.serverFunctions.spec.ts(new — 4 unit tests)admin.serverFunctionsto{}when absent.All 1329 existing unit tests still pass alongside the 4 new ones.
Lookup order
Backward compatibility
<RootLayout serverFunctions={...} />continues to win on key collisions.Cross-plugin key collisions
Plugins are expected to namespace their keys (e.g.
'@my-plugin/foo'). I considered emitting aconsole.warnat sanitize time when two plugins register the same key, but plugins mutateConfigone-by-one beforesanitizeConfigruns, so by the time sanitize sees the merged map the colliding entry has already been overwritten — the warning can't be reliably produced from the central pipeline. Happy to add a per-plugin merge helper if the team prefers that direction.Concrete consumer
A planned
@shefing/review-buttonplugin will use this registry to ship a'shefing/review-button:render-diff'server function without forcing every integrator to edit(payload)/layout.tsx.Companion proposal
Companion PR #16498 (issue #16496) exposes the Versions diff surface as
@payloadcms/next/views/diff. The two PRs are independent and can be reviewed/merged separately.