api-cms-payload
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePayload CMS Patterns
Payload CMS 模式
Quick Guide: Use Payload for code-first content management with TypeScript. Define collections and globals as config objects with typed fields, hooks, and access control functions. Prefer the Local API (,payload.find) for server-side operations. Always generate TypeScript types from your config. Use database adapters (Postgres or MongoDB) and never hardcode credentials. Access control functions receivepayload.createwith the authenticated user. Hooks run at the document lifecycle level (beforeChange, afterChange, etc.) and must not have side effects that block the request unless intentional.{ req }
<critical_requirements>
快速指南: 使用Payload进行基于TypeScript的代码优先内容管理。将集合和全局配置定义为包含类型化字段、钩子和访问控制函数的配置对象。服务器端操作优先使用Local API(、payload.find)。始终根据你的配置生成TypeScript类型。使用数据库适配器(Postgres或MongoDB),切勿硬编码凭据。访问控制函数会接收包含已认证用户的payload.create参数。钩子在文档生命周期层面运行(beforeChange、afterChange等),除非有意为之,否则不得包含会阻塞请求的副作用。{ req }
<critical_requirements>
CRITICAL: Before Using This Skill
重要提示:使用此技能前须知
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST define access control on every collection — open collections are a security risk)
(You MUST use the Local API (, ) for server-side data operations — it is zero-latency and fully typed)
payload.findpayload.create(You MUST generate TypeScript types with after every schema change)
payload generate:types(You MUST keep JSX/React component imports OUT of the Payload config file — separate config and UI concerns)
(You MUST use when calling the Local API on behalf of a user — the default is which bypasses all access control)
overrideAccess: falsetrue</critical_requirements>
Auto-detection: Payload, payload, payloadcms, @payloadcms, buildConfig, CollectionConfig, GlobalConfig, payload.config.ts, payload.find, payload.create, payload.update, payload.delete, payload.findByID, lexicalEditor, richText, beforeChange, afterChange, afterRead, beforeValidate, access control payload, upload collection, imageSizes, versions drafts
When to use:
- Configuring with database adapter, collections, and globals
payload.config.ts - Defining collection schemas with typed fields (text, richText, relationship, blocks, array, group, upload, select)
- Implementing access control functions (role-based, ownership-based, field-level)
- Writing collection hooks (beforeChange, afterChange, beforeRead, afterRead, beforeValidate, beforeDelete, afterDelete)
- Querying data via Local API, REST API, or GraphQL
- Setting up authentication collections with login, roles, and JWT
- Configuring uploads/media with image sizes and mime type restrictions
- Enabling versions and drafts on collections or globals
- Customizing the admin panel (groups, hidden collections, custom components)
Key patterns covered:
- setup with
payload.config.ts, database adapters, editor configbuildConfig - Collection config: slug, fields, hooks, access, auth, upload, versions, admin
- Field types: text, richText, relationship, upload, blocks, array, group, select, tabs, checkbox, date, number, email, code, json, point, radio, textarea, row, collapsible
- Access control: collection-level and field-level, returning boolean or Where query
- Hooks: beforeChange, afterChange, beforeRead, afterRead, beforeValidate, beforeDelete, afterDelete, beforeOperation, afterOperation
- Local API: ,
payload.find,payload.findByID,payload.create,payload.update,payload.deletepayload.count - REST API: auto-generated endpoints at
/api/{collection-slug} - Globals: singleton documents for site settings, navigation, footer
- Auth collections: , roles, login strategies
auth: true - Uploads: imageSizes, mimeTypes, media collections
- Versions and drafts:
versions: { drafts: true } - TypeScript type generation
When NOT to use:
- Simple key-value storage (use a database directly)
- Static site generation without content editing needs
- Applications that only need a REST API without an admin panel (use a plain API framework)
- Client-side data fetching patterns (Payload's Local API is server-only)
Detailed Resources:
- For decision frameworks and anti-patterns, see reference.md
Core Setup & Collections:
- examples/core.md — Config setup, collection definitions, field types, access control, hooks
Advanced Patterns:
- examples/advanced.md — Globals, versions/drafts, uploads/media, auth collections, Local API, REST API
<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入排序、、命名常量)import type
(你必须为每个集合定义访问控制——开放集合存在安全风险)
(服务器端数据操作必须使用Local API(、)——它具有零延迟且完全类型化)
payload.findpayload.create(每次修改架构后,必须使用生成TypeScript类型)
payload generate:types(必须将JSX/React组件导入排除在Payload配置文件之外——分离配置与UI关注点)
(代表用户调用Local API时,必须设置——默认值为,会绕过所有访问控制)
overrideAccess: falsetrue</critical_requirements>
自动检测关键词: Payload, payload, payloadcms, @payloadcms, buildConfig, CollectionConfig, GlobalConfig, payload.config.ts, payload.find, payload.create, payload.update, payload.delete, payload.findByID, lexicalEditor, richText, beforeChange, afterChange, afterRead, beforeValidate, access control payload, upload collection, imageSizes, versions drafts
适用场景:
- 配置,包含数据库适配器、集合和全局配置
payload.config.ts - 定义带有类型化字段(text、richText、relationship、blocks、array、group、upload、select)的集合架构
- 实现访问控制函数(基于角色、基于所有权、字段级)
- 编写集合钩子(beforeChange、afterChange、beforeRead、afterRead、beforeValidate、beforeDelete、afterDelete)
- 通过Local API、REST API或GraphQL查询数据
- 设置包含登录、角色和JWT的认证集合
- 配置带有图片尺寸和MIME类型限制的上传/媒体功能
- 在集合或全局配置上启用版本和草稿功能
- 自定义管理面板(分组、隐藏集合、自定义组件)
涵盖的核心模式:
- 使用、数据库适配器、编辑器配置搭建
buildConfigpayload.config.ts - 集合配置:slug、字段、钩子、访问控制、认证、上传、版本、管理面板
- 字段类型:text、richText、relationship、upload、blocks、array、group、select、tabs、checkbox、date、number、email、code、json、point、radio、textarea、row、collapsible
- 访问控制:集合级和字段级,返回布尔值或Where查询
- 钩子:beforeChange、afterChange、beforeRead、afterRead、beforeValidate、beforeDelete、afterDelete、beforeOperation、afterOperation
- Local API:、
payload.find、payload.findByID、payload.create、payload.update、payload.deletepayload.count - REST API:自动生成的端点
/api/{collection-slug} - 全局配置:用于站点设置、导航、页脚的单例文档
- 认证集合:、角色、登录策略
auth: true - 上传功能:imageSizes、mimeTypes、媒体集合
- 版本和草稿:
versions: { drafts: true } - TypeScript类型生成
不适用场景:
- 简单键值存储(直接使用数据库)
- 无需内容编辑的静态站点生成
- 仅需要REST API而无需管理面板的应用(使用普通API框架)
- 客户端数据获取模式(Payload的Local API仅支持服务器端)
详细资源:
- 决策框架和反模式请参考reference.md
核心设置与集合:
- examples/core.md — 配置搭建、集合定义、字段类型、访问控制、钩子
高级模式:
- examples/advanced.md — 全局配置、版本/草稿、上传/媒体、认证集合、Local API、REST API
<philosophy>
Philosophy
设计理念
Payload is a TypeScript-native headless CMS that treats your schema as code. Instead of clicking through a GUI to build content models, you define collections and globals as TypeScript config objects. Payload auto-generates an admin panel, REST API, GraphQL API, and a fully typed Local API from your config.
Core principles:
- Config-as-code -- Collections, globals, fields, hooks, and access control are all defined in TypeScript. Your schema is version-controlled, reviewable, and deployable like any other code.
- Three APIs from one config -- Every collection automatically gets a Local API (server-only, zero-latency), REST API (), and GraphQL API. The Local API is the primary interface for server-side operations.
/api/{slug} - Access control is mandatory -- Every collection should have explicit functions. By default, Payload denies access to unauthenticated users, but you must define who can do what. Access functions can return a boolean or a
accessquery to scope results.Where - Hooks for side effects -- Lifecycle hooks (beforeChange, afterChange, etc.) let you run logic at specific points in the document lifecycle. Keep hooks focused and avoid blocking operations unnecessarily.
- Database-agnostic -- Payload uses database adapters (Postgres or MongoDB). Your collections and fields are defined once and work with any supported database.
- Type generation -- Run to produce TypeScript interfaces from your config. This gives you end-to-end type safety from config to API responses.
payload generate:types
When to use Payload:
- Content-managed applications (blogs, e-commerce, marketing sites)
- Applications needing an admin panel with role-based access
- Projects requiring a typed CMS with version control over the schema
- Multi-tenant applications using access control to scope data per tenant
- Headless CMS backing a frontend framework
<patterns>
Payload是原生支持TypeScript的无头CMS,将你的架构视为代码。无需通过GUI点击构建内容模型,你只需将集合和全局配置定义为TypeScript配置对象。Payload会根据你的配置自动生成管理面板、REST API、GraphQL API以及完全类型化的Local API。
核心原则:
- 配置即代码 —— 集合、全局配置、字段、钩子和访问控制均通过TypeScript定义。你的架构像其他代码一样支持版本控制、代码评审和部署。
- 一份配置生成三个API —— 每个集合自动拥有Local API(仅服务器端、零延迟)、REST API()和GraphQL API。Local API是服务器端操作的主要接口。
/api/{slug} - 访问控制是强制性的 —— 每个集合都应有明确的函数。默认情况下,Payload会拒绝未认证用户的访问,但你必须定义谁可以执行哪些操作。访问控制函数可以返回布尔值或
access查询来限定结果范围。Where - 钩子处理副作用 —— 生命周期钩子(beforeChange、afterChange等)允许你在文档生命周期的特定阶段运行逻辑。保持钩子聚焦,避免不必要地阻塞操作。
- 数据库无关性 —— Payload使用数据库适配器(Postgres或MongoDB)。你的集合和字段只需定义一次,即可在所有支持的数据库上运行。
- 类型生成 —— 运行从你的配置生成TypeScript接口。这能为你提供从配置到API响应的端到端类型安全。
payload generate:types
使用Payload的场景:
- 内容管理应用(博客、电商、营销站点)
- 需要基于角色访问控制的管理面板的应用
- 需要类型化CMS且架构支持版本控制的项目
- 使用访问控制为每个租户限定数据范围的多租户应用
- 为前端框架提供支持的无头CMS
<patterns>
Core Patterns
核心模式
Pattern 1: payload.config.ts Setup
模式1:payload.config.ts 配置
The config is the entry point. It defines the database adapter, collections, globals, editor, and admin settings. Always use env vars for credentials.
typescript
const config = buildConfig({
db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }),
editor: lexicalEditor(),
collections: [Posts, Users, Media],
globals: [SiteSettings],
admin: { user: Users.slug },
typescript: { outputFile: "./src/payload-types.ts" },
secret: process.env.PAYLOAD_SECRET!,
});Never hardcode database URLs or secrets. Import collections from separate files. See examples/core.md for full Postgres and MongoDB adapter configs.
配置文件是入口点,定义了数据库适配器、集合、全局配置、编辑器和管理面板设置。始终使用环境变量存储凭据。
typescript
const config = buildConfig({
db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }),
editor: lexicalEditor(),
collections: [Posts, Users, Media],
globals: [SiteSettings],
admin: { user: Users.slug },
typescript: { outputFile: "./src/payload-types.ts" },
secret: process.env.PAYLOAD_SECRET!,
});切勿硬编码数据库URL或密钥。从单独文件导入集合。完整的Postgres和MongoDB适配器配置请参考examples/core.md。
Pattern 2: Collection Config
模式2:集合配置
Collections are the primary data model. Each generates a database table, admin UI, and API endpoints. Define access control per operation, use for admin display, and keep each collection in its own file.
useAsTitletypescript
const Posts: CollectionConfig = {
slug: "posts",
admin: { useAsTitle: "title" },
access: {
read: () => true,
create: ({ req: { user } }) => Boolean(user),
update: isAdminOrAuthor,
delete: isAdmin,
},
hooks: {
beforeChange: [setAuthorOnCreate],
afterChange: [revalidatePostCache],
},
versions: { drafts: true },
fields: [
{ name: "title", type: "text", required: true },
{ name: "content", type: "richText" },
{
name: "author",
type: "relationship",
relationTo: "users",
required: true,
},
],
};See examples/core.md for full collection config with all field types, sidebar positioning, and SEO tabs.
集合是主要的数据模型。每个集合会生成数据库表、管理UI和API端点。为每个操作定义访问控制,使用设置管理面板显示标题,并将每个集合放在单独文件中。
useAsTitletypescript
const Posts: CollectionConfig = {
slug: "posts",
admin: { useAsTitle: "title" },
access: {
read: () => true,
create: ({ req: { user } }) => Boolean(user),
update: isAdminOrAuthor,
delete: isAdmin,
},
hooks: {
beforeChange: [setAuthorOnCreate],
afterChange: [revalidatePostCache],
},
versions: { drafts: true },
fields: [
{ name: "title", type: "text", required: true },
{ name: "content", type: "richText" },
{
name: "author",
type: "relationship",
relationTo: "users",
required: true,
},
],
};包含所有字段类型、侧边栏定位和SEO标签的完整集合配置请参考examples/core.md。
Pattern 3: Access Control
模式3:访问控制
Access functions receive with the authenticated user. They return / or a query to scope results. Define reusable functions in a shared directory.
{ req }truefalseWhereaccess/typescript
// Return boolean for simple checks
const isAdmin: Access = ({ req: { user } }) => user?.role === "admin";
// Return Where query for scoped access — users see only their own documents
const isAdminOrSelf: Access = ({ req: { user } }) => {
if (!user) return false;
if (user.role === "admin") return true;
return { author: { equals: user.id } };
};Field-level access uses the same pattern on individual fields. See examples/core.md for reusable access functions and field-level access examples.
访问控制函数接收包含已认证用户的参数。返回/或查询来限定结果范围。在共享的目录中定义可复用函数。
{ req }truefalseWhereaccess/typescript
// 简单检查返回布尔值
const isAdmin: Access = ({ req: { user } }) => user?.role === "admin";
// 返回Where查询实现范围访问——用户仅能查看自己的文档
const isAdminOrSelf: Access = ({ req: { user } }) => {
if (!user) return false;
if (user.role === "admin") return true;
return { author: { equals: user.id } };
};字段级访问在单个字段配置上使用相同模式。可复用访问函数和字段级访问示例请参考examples/core.md。
Pattern 4: Collection Hooks
模式4:集合钩子
Hooks run at specific points in the document lifecycle. returns modified , is for non-blocking side effects. Use to access the Local API within hooks. Hooks receive a object to prevent infinite loops when hooks trigger other operations.
beforeChangedataafterChangereq.payloadcontexttypescript
beforeChange: [
({ data, operation, req }) => {
if (operation === "create" && req.user) data.author = req.user.id;
return data;
},
],
afterChange: [
({ doc, operation, req, context }) => {
if (context.skipRevalidation) return;
if (operation === "create") req.payload.logger.info(`Post created: ${doc.title}`);
},
],Never put blocking external API calls in -- use for non-critical side effects. See examples/core.md for hook patterns and examples/advanced.md for cross-collection hooks.
beforeChangeafterChange钩子在文档生命周期的特定阶段运行。返回修改后的,用于非阻塞副作用。在钩子内使用访问Local API。钩子接收对象以防止钩子触发其他操作时出现无限循环。
beforeChangedataafterChangereq.payloadcontexttypescript
beforeChange: [
({ data, operation, req }) => {
if (operation === "create" && req.user) data.author = req.user.id;
return data;
},
],
afterChange: [
({ doc, operation, req, context }) => {
if (context.skipRevalidation) return;
if (operation === "create") req.payload.logger.info(`Post created: ${doc.title}`);
},
],切勿在中放入阻塞性的外部API调用——将非关键副作用放在中或使用后台处理。钩子模式请参考examples/core.md,跨集合钩子请参考examples/advanced.md。
beforeChangeafterChangePattern 5: Field Types
模式5:字段类型
Payload provides typed fields: , , , , , , , , , , , , , . Structural fields compose to model any content shape:
textrichTextnumberselectcheckboxdateemailtextarearelationshipuploadjsoncodepointradio- group -- nested object with sub-fields
- array -- repeatable rows of same-shape fields
- blocks -- flexible content with multiple block types (use for custom TypeScript interface names)
interfaceName - tabs, row, collapsible -- admin-only layout helpers that do not affect data shape
Use blocks when editors choose from multiple block types for flexible page layouts. Use array when every row has the same fields. See examples/core.md for block definitions and reference.md for the complete field type table.
Payload提供类型化字段:、、、、、、、、、、、、、。结构化字段可组合成任意内容形态:
textrichTextnumberselectcheckboxdateemailtextarearelationshipuploadjsoncodepointradio- group —— 包含子字段的嵌套对象
- array —— 形状相同的可重复行
- blocks —— 支持多种块类型的灵活内容(使用自定义TypeScript接口名称)
interfaceName - tabs、row、collapsible —— 仅用于管理面板的布局辅助工具,不影响数据形态
当编辑器需要从多种块类型中选择以实现灵活页面布局时,使用blocks。当每行字段形状相同时,使用array。块定义请参考examples/core.md,完整字段类型表请参考reference.md。
Pattern 6: Local API
模式6:Local API
The Local API is the primary server-side interface -- zero-latency, fully typed, and executes hooks and access control. Always pass when operating on behalf of a user.
overrideAccess: falsetypescript
const payload = await getPayload({ config });
const result = await payload.find({
collection: "posts",
where: { status: { equals: "published" } },
sort: "-createdAt",
limit: 20,
depth: 1,
overrideAccess: false,
});Without , access control is completely bypassed (the default is ). See examples/advanced.md for full CRUD operations, bulk updates, and globals API.
</patterns>
overrideAccess: falsetrue<decision_framework>
Local API是主要的服务器端接口——零延迟、完全类型化,并会执行钩子和访问控制。代表用户操作时,始终传递。
overrideAccess: falsetypescript
const payload = await getPayload({ config });
const result = await payload.find({
collection: "posts",
where: { status: { equals: "published" } },
sort: "-createdAt",
limit: 20,
depth: 1,
overrideAccess: false,
});如果不设置,访问控制会被完全绕过(默认值为)。完整的CRUD操作、批量更新和全局API请参考examples/advanced.md。
</patterns>
overrideAccess: falsetrue<decision_framework>
Decision Framework
决策框架
Which API to Use
选择哪种API
Where is the code running?
+-- Server-side (API route, server component, script)
| +-- Local API (zero-latency, fully typed, preferred)
+-- External client (browser, mobile app, third-party)
| +-- REST API (/api/{collection-slug})
+-- GraphQL client
+-- GraphQL API (/api/graphql)代码运行位置?
+-- 服务器端(API路由、服务器组件、脚本)
| +-- Local API(零延迟、完全类型化、优先选择)
+-- 外部客户端(浏览器、移动应用、第三方)
| +-- REST API(/api/{collection-slug})
+-- GraphQL客户端
+-- GraphQL API(/api/graphql)Field Type Selection
字段类型选择
What kind of data?
+-- Single value
| +-- Short text --> text
| +-- Long text --> textarea
| +-- Rich content --> richText
| +-- Number --> number
| +-- Boolean --> checkbox
| +-- Date/time --> date
| +-- Email --> email
| +-- Coordinates --> point
| +-- Code snippet --> code
| +-- Arbitrary JSON --> json
+-- Choice from options
| +-- Single choice (dropdown) --> select
| +-- Single choice (visible) --> radio
| +-- Linked document --> relationship
| +-- File/image --> upload
+-- Nested structure
| +-- Fixed group of fields --> group
| +-- Repeatable rows (same shape) --> array
| +-- Flexible content (multiple block types) --> blocks
+-- Admin layout only (no data effect)
+-- Tabbed sections --> tabs
+-- Side-by-side fields --> row
+-- Collapsible section --> collapsible数据类型?
+-- 单个值
| +-- 短文本 --> text
| +-- 长文本 --> textarea
| +-- 富内容 --> richText
| +-- 数字 --> number
| +-- 布尔值 --> checkbox
| +-- 日期/时间 --> date
| +-- 邮箱 --> email
| +-- 坐标 --> point
| +-- 代码片段 --> code
| +-- 任意JSON --> json
+-- 选项选择
| +-- 单选(下拉) --> select
| +-- 单选(可见) --> radio
| +-- 关联文档 --> relationship
| +-- 文件/图片 --> upload
+-- 嵌套结构
| +-- 固定字段组 --> group
| +-- 可重复行(形状相同) --> array
| +-- 灵活内容(多种块类型) --> blocks
+-- 仅管理面板布局(不影响数据)
+-- 标签页区域 --> tabs
+-- 并排字段 --> row
+-- 可折叠区域 --> collapsibleAccess Control Strategy
访问控制策略
Who should access this data?
+-- Public (anyone) --> read: () => true
+-- Authenticated users only --> read: ({ req: { user } }) => Boolean(user)
+-- Admin only --> read: ({ req: { user } }) => user?.role === 'admin'
+-- Owner only --> read: return Where query matching user.id
+-- Mixed (public read, auth write) --> Different function per operation
+-- Field-level restriction --> access on individual field config谁可以访问这些数据?
+-- 公开(任何人) --> read: () => true
+-- 仅认证用户 --> read: ({ req: { user } }) => Boolean(user)
+-- 仅管理员 --> read: ({ req: { user } }) => user?.role === 'admin'
+-- 仅所有者 --> 返回匹配user.id的Where查询
+-- 混合模式(公开读取、认证写入) --> 为每个操作定义不同函数
+-- 字段级限制 --> 在单个字段配置上设置accessHooks vs Access Control
钩子 vs 访问控制
What do you need to do?
+-- Control WHO can do something --> Access control
+-- Control WHAT happens when they do it --> Hooks
+-- Validate data before saving --> beforeValidate hook or field validation
+-- Transform data before saving --> beforeChange hook
+-- Trigger side effects after saving --> afterChange hook
+-- Filter/transform output --> afterRead hook</decision_framework>
<red_flags>
你需要做什么?
+-- 控制谁可以执行操作 --> 访问控制
+-- 控制操作执行时发生什么 --> 钩子
+-- 保存前验证数据 --> beforeValidate钩子或字段验证
+-- 保存前转换数据 --> beforeChange钩子
+-- 保存后触发副作用 --> afterChange钩子
+-- 过滤/转换输出 --> afterRead钩子</decision_framework>
<red_flags>
RED FLAGS
风险警示
High Priority Issues:
- Missing access control on collections -- Without explicit functions, Payload denies all access to unauthenticated users but grants full access to any authenticated user. Always define explicit access rules.
access - default is
overrideAccessin Local API -- Everytrue,payload.find(), etc. call bypasses access control by default. Always passpayload.create()when operating on behalf of a user.overrideAccess: false - Importing JSX/React components in payload.config.ts -- Payload config runs in a Node context. Importing React components (even transitively) causes bundling errors. Keep config and UI imports completely separate.
- Hardcoded or database URL -- Use environment variables. The Payload secret is used to sign JWTs; hardcoding it is a security vulnerability.
secret
Medium Priority Issues:
- Using equivalent -- In the Local API, not specifying
select("*")returns all fields. Use theselectoption to fetch only needed fields for performance.select - Deep values -- Default depth is 2. High depth values cause cascading relationship queries. Set
depthordepth: 0unless you need deeply nested relationships.depth: 1 - Blocking hooks with external calls -- and
beforeChangehooks block the save operation. Move non-critical external API calls tobeforeValidateor use background processing.afterChange - Not running after schema changes -- Stale types lead to runtime errors that TypeScript should have caught at compile time.
payload generate:types
Common Mistakes:
- Deep-cloning collection configs -- strips hooks and access functions (they are functions, not serializable data). Use spread or Object.assign instead.
JSON.parse(JSON.stringify(config)) - Forgetting equivalent after create/update -- In the Local API,
.select()andpayload.createreturn the full document by default. Use thepayload.updateoption if you need specific fields.select - Using style access -- Define separate access functions for
FOR ALL,create,read,updateinstead of a single function. Different operations have different security requirements.delete - Monorepo version mismatches -- All packages in a monorepo must use the same version of ,
payload,@payloadcms/*,next, andreact. Mismatches cause subtle bundling errors.react-dom
Gotchas & Edge Cases:
- data is a partial on update -- On
beforeChangeoperations,updatecontains only the changed fields, not the full document. Usedatato access existing values.originalDoc - has no
beforeChangeon create -- The document ID is not available duringidon create operations. If you need the ID, usebeforeChange.afterChange - defaults -- Local API defaults to
overrideAccess(bypass access control). REST and GraphQL always enforce access control. This asymmetry is intentional but catches people off guard.true - Tabs, rows, and collapsibles do not affect data shape -- These are admin-only layout fields. A field inside a is stored at the top level of the document, not nested.
tab - Relationship depth cascading -- Setting on a collection with circular relationships can cause exponential query growth. Keep depth as low as possible.
depth: 3 - Auth collections auto-inject fields -- Collections with automatically get
auth: true,email,hash,salt, andloginAttemptsfields. Do not redefine them.lockUntil - Versions create a separate table -- Enabling creates a
versions: truetable (or equivalent). This can significantly increase storage for high-traffic collections._posts_versions - Access control queries run as SQL -- When an access function returns a
Wherequery instead of a boolean, it is appended to the database query. ComplexWherequeries can impact database performance.Where
</red_flags>
<critical_reminders>
高优先级问题:
- 集合缺少访问控制 —— 没有明确的函数时,Payload会拒绝未认证用户的所有访问,但会授予任何认证用户完全访问权限。始终定义明确的访问规则。
access - Local API中默认值为
overrideAccess—— 每次调用true、payload.find()等方法时,默认会绕过访问控制。代表用户操作时,始终传递payload.create()。overrideAccess: false - 在payload.config.ts中导入JSX/React组件 —— Payload配置运行在Node环境中。导入React组件(即使是间接导入)会导致打包错误。完全分离配置与UI导入。
- 硬编码或数据库URL —— 使用环境变量。Payload密钥用于签名JWT,硬编码会导致安全漏洞。
secret
中优先级问题:
- 使用等效于的操作 —— 在Local API中,不指定
select("*")会返回所有字段。使用select选项仅获取所需字段以提升性能。select - 过深的值 —— 默认深度为2。过高的深度值会导致关联查询级联。除非需要深度嵌套关联,否则设置
depth或depth: 0。depth: 1 - 钩子中包含阻塞性外部调用 —— 和
beforeChange钩子会阻塞保存操作。将非关键外部API调用移至beforeValidate或使用后台处理。afterChange - 修改架构后未运行—— 过时的类型会导致TypeScript本应在编译时捕获的运行时错误。
payload generate:types
常见错误:
- 深度克隆集合配置 —— 会丢失钩子和访问控制函数(它们是函数,不可序列化)。使用扩展运算符或Object.assign替代。
JSON.parse(JSON.stringify(config)) - 创建/更新后忘记等效的操作 —— 在Local API中,
.select()和payload.create默认返回完整文档。如果只需要特定字段,使用payload.update选项。select - 使用“全部允许”式访问控制 —— 为、
create、read、update分别定义访问控制函数,而非单个函数。不同操作有不同的安全要求。delete - 单仓库版本不匹配 —— 单仓库中的所有包必须使用相同版本的、
payload、@payloadcms/*、next和react。版本不匹配会导致难以察觉的打包错误。react-dom
陷阱与边缘情况:
- 更新时中的data是部分数据 —— 在
beforeChange操作中,update仅包含已更改的字段,而非完整文档。使用data访问现有值。originalDoc - 创建时中没有
beforeChange—— 创建操作的id阶段无法获取文档ID。如果需要ID,使用beforeChange。afterChange - 默认值差异 —— Local API默认值为
overrideAccess(绕过访问控制)。REST和GraphQL始终强制执行访问控制。这种不对称是有意设计的,但容易让人疏忽。true - Tabs、rows和collapsibles不影响数据形态 —— 这些是仅用于管理面板的布局字段。内的字段存储在文档顶层,而非嵌套结构中。
tab - 关联深度级联 —— 在存在循环关联的集合上设置会导致查询呈指数级增长。尽可能保持深度值较低。
depth: 3 - 认证集合自动注入字段 —— 设置的集合会自动获得
auth: true、email、hash、salt和loginAttempts字段。请勿重新定义这些字段。lockUntil - 版本会创建单独的表 —— 启用会创建
versions: true表(或等效表)。对于高流量集合,这会显著增加存储占用。_posts_versions - 访问控制的查询以SQL形式运行 —— 当访问控制函数返回
Where查询而非布尔值时,它会附加到数据库查询中。复杂的Where查询会影响数据库性能。Where
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
重要提醒
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST define access control on every collection — open collections are a security risk)
(You MUST use the Local API (, ) for server-side data operations — it is zero-latency and fully typed)
payload.findpayload.create(You MUST generate TypeScript types with after every schema change)
payload generate:types(You MUST keep JSX/React component imports OUT of the Payload config file — separate config and UI concerns)
(You MUST use when calling the Local API on behalf of a user — the default is which bypasses all access control)
overrideAccess: falsetrueFailure to follow these rules will create security vulnerabilities, type-unsafe operations, and bundling errors.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入排序、、命名常量)import type
(你必须为每个集合定义访问控制——开放集合存在安全风险)
(服务器端数据操作必须使用Local API(、)——它具有零延迟且完全类型化)
payload.findpayload.create(每次修改架构后,必须使用生成TypeScript类型)
payload generate:types(必须将JSX/React组件导入排除在Payload配置文件之外——分离配置与UI关注点)
(代表用户调用Local API时,必须设置——默认值为,会绕过所有访问控制)
overrideAccess: falsetrue不遵守这些规则会导致安全漏洞、类型不安全操作和打包错误。
</critical_reminders>