Skip to content

refactor!: consolidate request creation APIs - #18287

Merged
jacobsfletch merged 16 commits into
mainfrom
refactor/payload-req-creation
Sep 25, 2026
Merged

jacobsfletch merged 16 commits into
mainfrom
refactor/payload-req-creation

Conversation

@jacobsfletch

@jacobsfletch jacobsfletch commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Follow up to #17690

Consolidates PayloadRequest creation around a canonical createPayloadRequest function.

Payload currently exposes three overlapping paths for constructing a PayloadRequest:

  • createLocalReq for local operations
  • createPayloadRequest to convert incoming WebRequest objects
  • initReq for admin rendering

createPayloadRequest and initReq ultimately delegate to createLocalReq, but their names obscure that relationship. createPayloadRequest sounds like the general primitive despite being specific to Web requests, while initReq does not communicate that it prepares the complete admin context in addition to creating the req.

The new names describe each functions purpose much more clearly to remove any ambiguity or confusion.

Breaking Changes

Consumers that directly import request helpers, internal admin context types, or provide a custom Root layout adapter must migrate the renamed APIs.

Old name New name Reasoning
createLocalReq createPayloadRequest Not just meant for the Local API
createPayloadRequest createPayloadRequestFromWebRequest Verbose but explicit
initReq initAdminContext Kept "init" to signal that this is called at the start of the request lifecycle

Types have also been renamed to match:

Old name New name
InitReqArgs InitAdminContextArgs
InitReqResult AdminContext
InitReqCache AdminContextCache
InitReqPartialResult PartialAdminContext

Local request creation now accepts payload in the options object:

-import { createLocalReq } from 'payload'
+import { createPayloadRequest } from 'payload'

-const req = await createLocalReq(options, payload)
+const req = await createPayloadRequest({ ...options, payload })

Web request conversion uses its role-specific name:

-import { createPayloadRequest } from 'payload'
+import { createPayloadRequestFromWebRequest } from 'payload'

-const req = await createPayloadRequest({ config, request })
+const req = await createPayloadRequestFromWebRequest({ config, request })

Custom Root layout adapters must rename their injected admin context callback:

 <RootLayout
-  initReq={initReq}
+  initAdminContext={initAdminContext}
 />

The migration guide documents options that require manual handling, including complex expressions and private imports of CreateLocalReqOptions.

Codemod

To migrate automatically, there's a codemod for this change available by running:

npx @payloadcms/codemod --transform migrate-payload-request-creation

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 215.27 KB ⚠️ +27 B (+0.0%)
packages/payload/meta_index.json esbuild/index.js 1.86 MB ⚠️ +402 B (+0.0%)
packages/payload/meta_shared.json esbuild/exports/shared.js 553.18 KB ✅ No change
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 285.41 KB ✅ No change
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 36.54 KB ✅ No change
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 18.95 KB ✅ No change
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████████▋ }}}$ 98.7%, 210.74 KB
dist/adapters/router.js ${{\color{Goldenrod}{ }}}$ 0.3%, 718 B
dist/adapters/server.js ${{\color{Goldenrod}{ }}}$ 0.2%, 533 B
dist/adapters/layout.js ${{\color{Goldenrod}{ }}}$ 0.2%, 529 B
dist/adapters/views.js ${{\color{Goldenrod}{ }}}$ 0.2%, 324 B
dist/utilities/initAdminContext.js ${{\color{Goldenrod}{ }}}$ 0.1%, 315 B
dist/utilities/selectiveCache.js ${{\color{Goldenrod}{ }}}$ 0.1%, 263 B
dist/esbuildEntry.js ${{\color{Goldenrod}{ }}}$ 0.0%, 0 B

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ██████████████████ }}}$ 72.0%, 1.33 MB
dist/collections/operations ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 54.65 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 46.15 KB
dist/auth/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 16.95 KB
dist/globals/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 16.18 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 16.02 KB
dist/queues/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 14.31 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 13.65 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 11.88 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 10.82 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 10.50 KB
dist/cli/commands ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 9.92 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 8.76 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 8.38 KB
dist/database/migrations ${{\color{Goldenrod}{ }}}$ 0.4%, 8.15 KB
dist/index.js ${{\color{Goldenrod}{ }}}$ 0.4%, 7.79 KB
dist/hierarchy/utils ${{\color{Goldenrod}{ }}}$ 0.4%, 7.66 KB
dist/auth/strategies ${{\color{Goldenrod}{ }}}$ 0.4%, 7.36 KB
dist/utilities/entityInputSchema ${{\color{Goldenrod}{ }}}$ 0.4%, 7.34 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ }}}$ 0.4%, 7.15 KB
(other) ${{\color{Goldenrod}{ ███████ }}}$ 28.0%, 517.63 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ██████████████████████▏ }}}$ 88.8%, 486.64 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▌ }}}$ 2.0%, 10.79 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 5.83 KB
dist/utilities/traverseFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 4.34 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.52 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.42 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.13 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.79 KB
dist/config/client.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.56 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ }}}$ 0.3%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ }}}$ 0.3%, 1.42 KB
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ }}}$ 0.2%, 1.04 KB
dist/globals/config ${{\color{Goldenrod}{ }}}$ 0.2%, 939 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ }}}$ 0.2%, 850 B
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ }}}$ 0.1%, 794 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.1%, 713 B
dist/auth/extractJWT.js ${{\color{Goldenrod}{ }}}$ 0.1%, 696 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.1%, 651 B
dist/utilities/fieldPath.js ${{\color{Goldenrod}{ }}}$ 0.1%, 639 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.1%, 632 B
(other) ${{\color{Goldenrod}{ ██▊ }}}$ 11.2%, 61.67 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███▎ }}}$ 13.2%, 37.20 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▉ }}}$ 11.8%, 33.21 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.7%, 33.01 KB
dist/features/table ${{\color{Goldenrod}{ ██▍ }}}$ 9.6%, 27.18 KB
dist/features/link ${{\color{Goldenrod}{ █▋ }}}$ 6.7%, 18.82 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.2%, 17.45 KB
dist/features/upload ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 14.24 KB
dist/features/textState ${{\color{Goldenrod}{ ▉ }}}$ 3.9%, 11.08 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 10.02 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.3%, 9.43 KB
dist/features/converters ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 8.40 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 8.12 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 7.40 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.14 KB
dist/features/lists ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 3.67 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.28 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.23 KB
dist/features/horizontalRule ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.18 KB
dist/field/Field.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 2.88 KB
dist/lexical/nodes ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.66 KB
(other) ${{\color{Goldenrod}{ █████████████████████▋ }}}$ 86.8%, 245.00 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/exports/client ${{\color{Goldenrod}{ █████████████████████████ }}}$ 100.0%, 26.90 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ ███████▋ }}}$ 30.5%, 5.57 KB
../../node_modules ${{\color{Goldenrod}{ ███▌ }}}$ 14.5%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██ }}}$ 8.3%, 1.51 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ █▊ }}}$ 7.2%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▏ }}}$ 4.7%, 866 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ █ }}}$ 4.2%, 762 B
dist/utilities/api.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 756 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 745 B
dist/elements/Translation ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 440 B
dist/utilities/traverseForLocalizedFields.js ${{\color{Goldenrod}{ ▌ }}}$ 2.3%, 419 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 339 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 338 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 329 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 232 B
dist/layouts/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 230 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 158 B
dist/forms/Form ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
(other) ${{\color{Goldenrod}{ █████████████████▍ }}}$ 69.5%, 12.68 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

@jacobsfletch
jacobsfletch force-pushed the refactor/payload-req-creation branch from 9e94248 to daa1f1a Compare September 24, 2026 15:30
@jacobsfletch
jacobsfletch marked this pull request as ready for review September 25, 2026 01:22
@jacobsfletch jacobsfletch changed the title refactor!: consolidate PayloadRequest creation refactor!: consolidate request creation APIs Sep 25, 2026
@jacobsfletch
jacobsfletch merged commit 3eb7b9f into main Sep 25, 2026
287 checks passed
@jacobsfletch
jacobsfletch deleted the refactor/payload-req-creation branch September 25, 2026 13:11
Ducksss added a commit to Ducksss/payload-components that referenced this pull request Oct 4, 2026
…resh smoke

create-payload-app 4 renamed --version to --payload-version. Its argument
parser is permissive, so the old flag was silently dropped and a smoke
pinned to 4.0.0-canary.37 scaffolded whatever the `canary` dist-tag
resolved to. Pass the flag each major reads, and both for a dist-tag.

create-payload-app 4 also downloads its template from the payloadcms/payload
main branch instead of the release it installs. Main had already renamed
createLocalReq (payloadcms/payload#18287, after canary.37 shipped), so the
pinned scaffold failed tsc in the template's own seed route. Pin a v4
template to the release tag with --branch when that tag exists; v3 keeps
its CLI's 3.x branch and needs no network check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ducksss added a commit to Ducksss/payload-components that referenced this pull request Oct 4, 2026
* cli(feat): accept Payload 4 projects alongside Payload 3

Payload 4 is in canary (4.0.0-canary.37) and every install into a v4
project was refused. Allow major 4 in both support-matrix targets and in
every manifest (payloadMajors [3, 4], payload peer "^3.0.0 || ^4.0.0-0"),
the component template, and the `new` scaffold.

Widening the ranges alone was not enough: semver.intersects without
includePrerelease tests each comparator on its own, so "<5.0.0-0" rejects
4.0.0-canary.37 and a pinned prerelease never intersects "^4.0.0-0". The
"-0" bounds on every required range keep prereleases inside their major.

detectProject now names an unsupported Payload or Next.js major instead of
reporting "Unsupported project shape", which is what a v4 project got.

The base Pages collection gains an explicit readVersions (signed-in only).
Payload 4 inherits read access for versions (payloadcms/payload#17634), so
visitors could list every previously published version. Payload 3 already
requires a user here, so this is a no-op on v3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cli(fix): scaffold the pinned Payload 4 version and template in the fresh smoke

create-payload-app 4 renamed --version to --payload-version. Its argument
parser is permissive, so the old flag was silently dropped and a smoke
pinned to 4.0.0-canary.37 scaffolded whatever the `canary` dist-tag
resolved to. Pass the flag each major reads, and both for a dist-tag.

create-payload-app 4 also downloads its template from the payloadcms/payload
main branch instead of the release it installs. Main had already renamed
createLocalReq (payloadcms/payload#18287, after canary.37 shipped), so the
pinned scaffold failed tsc in the template's own seed route. Pin a v4
template to the release tag with --branch when that tag exists; v3 keeps
its CLI's 3.x branch and needs no network check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci(feat): add a non-blocking nightly Payload canary smoke

The nightly fresh smoke follows Payload `latest`, so nothing exercised the
next major before it ships. Run the same bare and website scenarios against
`npm view payload@canary version` on Node.js 24 (Payload 4 requires 24.15+),
schedule-only and with continue-on-error, outside pr-gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cli(fix): widen related-posts to Payload 4 and hold manifests to the support matrix

related-posts (#600) landed declaring Payload 3 only, so once Payload 4 is
allowed it would be the one component refusing v4 projects. Give it the
same supports and peer range as every other manifest; manifest-only, so the
versioned-source contract needs no version bump.

payload-components-support-matrix.int.spec.ts makes this class of drift
fail: every manifest, the component template and the `new` scaffold must
declare exactly the Payload and Next.js majors their targets allow, with peer
ranges that cover each major's stable line and the newest Payload major's
prereleases. A v3-only fixture keeps the guard from passing vacuously.
buildManifest is exported so the scaffold's output can be checked directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🚀 This is included in version v4.0.0-canary.38

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants