payload

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Payload Application Development

Payload 应用开发

Payload is a Next.js native CMS with TypeScript-first architecture, providing admin panel, database management, REST/GraphQL APIs, authentication, and file storage.
Payload 是一款基于Next.js的原生CMS,采用TypeScript优先架构,提供管理面板、数据库管理、REST/GraphQL API、身份验证和文件存储功能。

Quick Reference

快速参考

TaskSolutionDetails
Auto-generate slugs
slugField()
FIELDS.md#slug-field-helper
Restrict content by userAccess control with queryACCESS-CONTROL.md#row-level-security-with-complex-queries
Local API user ops
user
+
overrideAccess: false
QUERIES.md#access-control-in-local-api
Draft/publish workflow
versions: { drafts: true }
COLLECTIONS.md#versioning--drafts
Computed fields
virtual: true
with afterRead
FIELDS.md#virtual-fields
Conditional fields
admin.condition
FIELDS.md#conditional-fields
Custom field validation
validate
function
FIELDS.md#text-field
Filter relationship list
filterOptions
on field
FIELDS.md#relationship
Select specific fields
select
parameter
QUERIES.md#local-api
Auto-set author/datesbeforeChange hookHOOKS.md#collection-hooks
Prevent hook loops
req.context
check
HOOKS.md#hook-context
Cascading deletesbeforeDelete hookHOOKS.md#collection-hooks
Geospatial queries
point
field with
near
/
within
FIELDS.md#point-geolocation
Reverse relationships
join
field type
FIELDS.md#join-fields
Next.js revalidationContext control in afterChangeHOOKS.md#nextjs-revalidation-with-context-control
Query by relationshipNested property syntaxQUERIES.md#nested-properties
Complex queriesAND/OR logicQUERIES.md#andor-logic
TransactionsPass
req
to operations
ADAPTERS.md#threading-req-through-operations
Background jobsJobs queue with tasksADVANCED.md#jobs-queue
Custom API routesCollection custom endpointsADVANCED.md#custom-endpoints
Cloud storageStorage adapter pluginsADAPTERS.md#storage-adapters
Multi-language
localization
config +
localized: true
ADVANCED.md#localization
Create plugin
(options) => (config) => Config
PLUGIN-DEVELOPMENT.md#plugin-architecture
Plugin package setupPackage structure with SWCPLUGIN-DEVELOPMENT.md#plugin-package-structure
Add fields to collectionMap collections, spread fieldsPLUGIN-DEVELOPMENT.md#adding-fields-to-collections
Plugin hooksPreserve existing hooks in arrayPLUGIN-DEVELOPMENT.md#adding-hooks
Check field typeType guard functionsFIELD-TYPE-GUARDS.md
任务解决方案详情
自动生成slug
slugField()
FIELDS.md#slug-field-helper
按用户限制内容访问带查询条件的访问控制ACCESS-CONTROL.md#row-level-security-with-complex-queries
Local API 用户操作
user
+
overrideAccess: false
QUERIES.md#access-control-in-local-api
草稿/发布工作流
versions: { drafts: true }
COLLECTIONS.md#versioning--drafts
计算字段结合
virtual: true
与afterRead钩子
FIELDS.md#virtual-fields
条件字段
admin.condition
FIELDS.md#conditional-fields
自定义字段验证
validate
函数
FIELDS.md#text-field
过滤关联列表字段上的
filterOptions
参数
FIELDS.md#relationship
选择特定字段
select
参数
QUERIES.md#local-api
自动设置作者/日期beforeChange 钩子HOOKS.md#collection-hooks
防止钩子循环
req.context
检查
HOOKS.md#hook-context
级联删除beforeDelete 钩子HOOKS.md#collection-hooks
地理空间查询
point
字段结合
near
/
within
操作符
FIELDS.md#point-geolocation
反向关联
join
字段类型
FIELDS.md#join-fields
Next.js 重新验证afterChange钩子中的上下文控制HOOKS.md#nextjs-revalidation-with-context-control
按关联关系查询嵌套属性语法QUERIES.md#nested-properties
复杂查询AND/OR 逻辑QUERIES.md#andor-logic
事务处理向操作传递
req
参数
ADAPTERS.md#threading-req-through-operations
后台任务带任务的作业队列ADVANCED.md#jobs-queue
自定义API路由集合自定义端点ADVANCED.md#custom-endpoints
云存储存储适配器插件ADAPTERS.md#storage-adapters
多语言支持
localization
配置 +
localized: true
ADVANCED.md#localization
创建插件
(options) => (config) => Config
格式
PLUGIN-DEVELOPMENT.md#plugin-architecture
插件包设置基于SWC的包结构PLUGIN-DEVELOPMENT.md#plugin-package-structure
向集合添加字段映射集合并展开字段PLUGIN-DEVELOPMENT.md#adding-fields-to-collections
插件钩子保留数组中的现有钩子PLUGIN-DEVELOPMENT.md#adding-hooks
检查字段类型类型守卫函数FIELD-TYPE-GUARDS.md

Quick Start

快速开始

bash
npx create-payload-app@latest my-app
cd my-app
pnpm dev
bash
npx create-payload-app@latest my-app
cd my-app
pnpm dev

Minimal Config

最小配置

ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { fileURLToPath } from 'url'

const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

export default buildConfig({
  admin: {
    user: 'users',
    importMap: {
      baseDir: path.resolve(dirname),
    },
  },
  collections: [Users, Media],
  editor: lexicalEditor(),
  secret: process.env.PAYLOAD_SECRET,
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
  },
  db: mongooseAdapter({
    url: process.env.DATABASE_URL,
  }),
})
ts
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { fileURLToPath } from 'url'

const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

export default buildConfig({
  admin: {
    user: 'users',
    importMap: {
      baseDir: path.resolve(dirname),
    },
  },
  collections: [Users, Media],
  editor: lexicalEditor(),
  secret: process.env.PAYLOAD_SECRET,
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
  },
  db: mongooseAdapter({
    url: process.env.DATABASE_URL,
  }),
})

Essential Patterns

核心模式

Basic Collection

基础集合

ts
import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  admin: {
    useAsTitle: 'title',
    defaultColumns: ['title', 'author', 'status', 'createdAt'],
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'slug', type: 'text', unique: true, index: true },
    { name: 'content', type: 'richText' },
    { name: 'author', type: 'relationship', relationTo: 'users' },
  ],
  timestamps: true,
}
For more collection patterns (auth, upload, drafts, live preview), see COLLECTIONS.md.
ts
import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  admin: {
    useAsTitle: 'title',
    defaultColumns: ['title', 'author', 'status', 'createdAt'],
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'slug', type: 'text', unique: true, index: true },
    { name: 'content', type: 'richText' },
    { name: 'author', type: 'relationship', relationTo: 'users' },
  ],
  timestamps: true,
}
更多集合模式(身份验证、上传、草稿、实时预览)请查看 COLLECTIONS.md

Common Fields

常用字段

ts
// Text field
{ name: 'title', type: 'text', required: true }

// Relationship
{ name: 'author', type: 'relationship', relationTo: 'users', required: true }

// Rich text
{ name: 'content', type: 'richText', required: true }

// Select
{ name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft' }

// Upload
{ name: 'image', type: 'upload', relationTo: 'media' }
For all field types (array, blocks, point, join, virtual, conditional, etc.), see FIELDS.md.
ts
// 文本字段
{ name: 'title', type: 'text', required: true }

// 关联字段
{ name: 'author', type: 'relationship', relationTo: 'users', required: true }

// 富文本字段
{ name: 'content', type: 'richText', required: true }

// 选择字段
{ name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft' }

// 上传字段
{ name: 'image', type: 'upload', relationTo: 'media' }
所有字段类型(数组、块、点、关联、虚拟、条件等)请查看 FIELDS.md

Hook Example

钩子示例

ts
export const Posts: CollectionConfig = {
  slug: 'posts',
  hooks: {
    beforeChange: [
      async ({ data, operation }) => {
        if (operation === 'create') {
          data.slug = slugify(data.title)
        }
        return data
      },
    ],
  },
  fields: [{ name: 'title', type: 'text' }],
}
For all hook patterns, see HOOKS.md. For access control, see ACCESS-CONTROL.md.
ts
export const Posts: CollectionConfig = {
  slug: 'posts',
  hooks: {
    beforeChange: [
      async ({ data, operation }) => {
        if (operation === 'create') {
          data.slug = slugify(data.title)
        }
        return data
      },
    ],
  },
  fields: [{ name: 'title', type: 'text' }],
}
所有钩子模式请查看 HOOKS.md。访问控制相关内容请查看 ACCESS-CONTROL.md

Access Control with Type Safety

类型安全的访问控制

ts
import type { Access } from 'payload'
import type { User } from '@/payload-types'

// Type-safe access control
export const adminOnly: Access = ({ req }) => {
  const user = req.user as User
  return user?.roles?.includes('admin') || false
}

// Row-level access control
export const ownPostsOnly: Access = ({ req }) => {
  const user = req.user as User
  if (!user) return false
  if (user.roles?.includes('admin')) return true

  return {
    author: { equals: user.id },
  }
}
ts
import type { Access } from 'payload'
import type { User } from '@/payload-types'

// 类型安全的访问控制
export const adminOnly: Access = ({ req }) => {
  const user = req.user as User
  return user?.roles?.includes('admin') || false
}

// 行级访问控制
export const ownPostsOnly: Access = ({ req }) => {
  const user = req.user as User
  if (!user) return false
  if (user.roles?.includes('admin')) return true

  return {
    author: { equals: user.id },
  }
}

Query Example

查询示例

ts
// Local API
const posts = await payload.find({
  collection: 'posts',
  where: {
    status: { equals: 'published' },
    'author.name': { contains: 'john' },
  },
  depth: 2,
  limit: 10,
  sort: '-createdAt',
})

// Query with populated relationships
const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 2, // Populates relationships (default is 2)
})
// Returns: { author: { id: "user123", name: "John" } }

// Without depth, relationships return IDs only
const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 0,
})
// Returns: { author: "user123" }
For all query operators and REST/GraphQL examples, see QUERIES.md.
ts
// Local API
const posts = await payload.find({
  collection: 'posts',
  where: {
    status: { equals: 'published' },
    'author.name': { contains: 'john' },
  },
  depth: 2,
  limit: 10,
  sort: '-createdAt',
})

// 带关联数据填充的查询
const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 2, // 填充关联数据(默认值为2)
})
// 返回结果: { author: { id: "user123", name: "John" } }

// 不填充关联数据,仅返回ID
const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 0,
})
// 返回结果: { author: "user123" }
所有查询操作符及REST/GraphQL示例请查看 QUERIES.md

Getting Payload Instance

获取Payload实例

ts
// In API routes (Next.js)
import { getPayload } from 'payload'
import config from '@payload-config'

export async function GET() {
  const payload = await getPayload({ config })

  const posts = await payload.find({
    collection: 'posts',
  })

  return Response.json(posts)
}

// In Server Components
import { getPayload } from 'payload'
import config from '@payload-config'

export default async function Page() {
  const payload = await getPayload({ config })
  const { docs } = await payload.find({ collection: 'posts' })

  return <div>{docs.map(post => <h1 key={post.id}>{post.title}</h1>)}</div>
}
ts
// 在API路由中(Next.js)
import { getPayload } from 'payload'
import config from '@payload-config'

export async function GET() {
  const payload = await getPayload({ config })

  const posts = await payload.find({
    collection: 'posts',
  })

  return Response.json(posts)
}

// 在Server Components中
import { getPayload } from 'payload'
import config from '@payload-config'

export default async function Page() {
  const payload = await getPayload({ config })
  const { docs } = await payload.find({ collection: 'posts' })

  return <div>{docs.map(post => <h1 key={post.id}>{post.title}</h1>)}</div>
}

Logger Usage

日志器使用

ts
// ✅ Valid: single string
payload.logger.error('Something went wrong')

// ✅ Valid: object with msg and err
payload.logger.error({ msg: 'Failed to process', err: error })

// ❌ Invalid: don't pass error as second argument
payload.logger.error('Failed to process', error)

// ❌ Invalid: use `err` not `error`, use `msg` not `message`
payload.logger.error({ message: 'Failed', error: error })
ts
// ✅ 正确用法:单个字符串
payload.logger.error('Something went wrong')

// ✅ 正确用法:包含msg和err的对象
payload.logger.error({ msg: 'Failed to process', err: error })

// ❌ 错误用法:不要将错误作为第二个参数传递
payload.logger.error('Failed to process', error)

// ❌ 错误用法:使用`err`而非`error`,使用`msg`而非`message`
payload.logger.error({ message: 'Failed', error: error })

Security Pitfalls

安全陷阱

1. Local API Access Control (CRITICAL)

1. Local API 访问控制(关键)

By default, Local API operations bypass ALL access control, even when passing a user.
ts
// ❌ SECURITY BUG: Passes user but ignores their permissions
await payload.find({
  collection: 'posts',
  user: someUser, // Access control is BYPASSED!
})

// ✅ SECURE: Actually enforces the user's permissions
await payload.find({
  collection: 'posts',
  user: someUser,
  overrideAccess: false, // REQUIRED for access control
})
When to use each:
  • overrideAccess: true
    (default) - Server-side operations you trust (cron jobs, system tasks)
  • overrideAccess: false
    - When operating on behalf of a user (API routes, webhooks)
See QUERIES.md#access-control-in-local-api.
默认情况下,Local API操作会绕过所有访问控制,即使传递了用户信息。
ts
// ❌ 安全漏洞:传递了用户但忽略其权限
await payload.find({
  collection: 'posts',
  user: someUser, // 访问控制被绕过!
})

// ✅ 安全写法:实际强制执行用户权限
await payload.find({
  collection: 'posts',
  user: someUser,
  overrideAccess: false, // 启用访问控制必须设置此参数
})
使用场景:
  • overrideAccess: true
    (默认值)- 可信的服务器端操作(定时任务、系统任务)
  • overrideAccess: false
    - 代表用户执行操作时(API路由、Webhook)
详情请查看 QUERIES.md#access-control-in-local-api

2. Transaction Failures in Hooks

2. 钩子中的事务失败

Nested operations in hooks without
req
break transaction atomicity.
ts
// ❌ DATA CORRUPTION RISK: Separate transaction
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.create({
        collection: 'audit-log',
        data: { docId: doc.id },
        // Missing req - runs in separate transaction!
      })
    },
  ]
}

// ✅ ATOMIC: Same transaction
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.create({
        collection: 'audit-log',
        data: { docId: doc.id },
        req, // Maintains atomicity
      })
    },
  ]
}
See ADAPTERS.md#threading-req-through-operations.
钩子中未传递
req
的嵌套操作会破坏事务原子性。
ts
// ❌ 数据损坏风险:独立事务
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.create({
        collection: 'audit-log',
        data: { docId: doc.id },
        // 缺少req - 在独立事务中运行!
      })
    },
  ]
}

// ✅ 原子性:同一事务
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.create({
        collection: 'audit-log',
        data: { docId: doc.id },
        req, // 保持原子性
      })
    },
  ]
}
详情请查看 ADAPTERS.md#threading-req-through-operations

3. Infinite Hook Loops

3. 无限钩子循环

Hooks triggering operations that trigger the same hooks create infinite loops.
ts
// ❌ INFINITE LOOP
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.update({
        collection: 'posts',
        id: doc.id,
        data: { views: doc.views + 1 },
        req,
      }) // Triggers afterChange again!
    },
  ]
}

// ✅ SAFE: Use context flag
hooks: {
  afterChange: [
    async ({ doc, req, context }) => {
      if (context.skipHooks) return

      await req.payload.update({
        collection: 'posts',
        id: doc.id,
        data: { views: doc.views + 1 },
        context: { skipHooks: true },
        req,
      })
    },
  ]
}
See HOOKS.md#context.
钩子触发的操作再次触发相同钩子会导致无限循环。
ts
// ❌ 无限循环
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.update({
        collection: 'posts',
        id: doc.id,
        data: { views: doc.views + 1 },
        req,
      }) // 再次触发afterChange钩子!
    },
  ]
}

// ✅ 安全写法:使用上下文标志
hooks: {
  afterChange: [
    async ({ doc, req, context }) => {
      if (context.skipHooks) return

      await req.payload.update({
        collection: 'posts',
        id: doc.id,
        data: { views: doc.views + 1 },
        context: { skipHooks: true },
        req,
      })
    },
  ]
}
详情请查看 HOOKS.md#context

Project Structure

项目结构

txt
src/
├── app/
│   ├── (frontend)/
│   │   └── page.tsx
│   └── (payload)/
│       └── admin/[[...segments]]/page.tsx
├── collections/
│   ├── Posts.ts
│   ├── Media.ts
│   └── Users.ts
├── globals/
│   └── Header.ts
├── components/
│   └── CustomField.tsx
├── hooks/
│   └── slugify.ts
└── payload.config.ts
txt
src/
├── app/
│   ├── (frontend)/
│   │   └── page.tsx
│   └── (payload)/
│       └── admin/[[...segments]]/page.tsx
├── collections/
│   ├── Posts.ts
│   ├── Media.ts
│   └── Users.ts
├── globals/
│   └── Header.ts
├── components/
│   └── CustomField.tsx
├── hooks/
│   └── slugify.ts
└── payload.config.ts

Type Generation

类型生成

ts
// payload.config.ts
export default buildConfig({
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
  },
  // ...
})

// Usage
import type { Post, User } from '@/payload-types'
ts
// payload.config.ts
export default buildConfig({
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
  },
  // ...
})

// 使用示例
import type { Post, User } from '@/payload-types'

Reference Documentation

参考文档

  • FIELDS.md - All field types, validation, admin options
  • FIELD-TYPE-GUARDS.md - Type guards for runtime field type checking and narrowing
  • COLLECTIONS.md - Collection configs, auth, upload, drafts, live preview
  • HOOKS.md - Collection hooks, field hooks, context patterns
  • ACCESS-CONTROL.md - Collection, field, global access control, RBAC, multi-tenant
  • ACCESS-CONTROL-ADVANCED.md - Context-aware, time-based, subscription-based access, factory functions, templates
  • QUERIES.md - Query operators, Local/REST/GraphQL APIs
  • ENDPOINTS.md - Custom API endpoints: authentication, helpers, request/response patterns
  • ADAPTERS.md - Database, storage, email adapters, transactions
  • ADVANCED.md - Authentication, jobs, endpoints, components, plugins, localization
  • PLUGIN-DEVELOPMENT.md - Plugin architecture, monorepo structure, patterns, best practices
  • FIELDS.md - 所有字段类型、验证、管理端选项
  • FIELD-TYPE-GUARDS.md - 用于运行时字段类型检查和收窄的类型守卫
  • COLLECTIONS.md - 集合配置、身份验证、上传、草稿、实时预览
  • HOOKS.md - 集合钩子、字段钩子、上下文模式
  • ACCESS-CONTROL.md - 集合、字段、全局访问控制、RBAC、多租户
  • ACCESS-CONTROL-ADVANCED.md - 上下文感知、基于时间、基于订阅的访问控制、工厂函数、模板
  • QUERIES.md - 查询操作符、Local/REST/GraphQL API
  • ENDPOINTS.md - 自定义API端点:身份验证、助手函数、请求/响应模式
  • ADAPTERS.md - 数据库、存储、邮件适配器、事务
  • ADVANCED.md - 身份验证、作业、端点、组件、插件、多语言
  • PLUGIN-DEVELOPMENT.md - 插件架构、单仓库结构、模式、最佳实践

Resources

资源