mcpField
P
payload

payload

github.com/jbmanllr开发149 次安装39 周前更新
代码仓库

关于 payload

面向 AI 编码 Agent 的 Payload CMS 开发技能,提供配置、查询、访问控制、钩子和插件开发参考,适用于构建 Next.js 原生 CMS 应用。

主要功能

  1. 01集合与字段配置模式
  2. 02查询和访问控制参考
  3. 03钩子与事务实践
  4. 04API 端点及适配器说明
  5. 05插件与本地化开发指导

适用场景

  1. 01配置 CMS 集合、字段和草稿发布
  2. 02为用户操作实现访问控制
  3. 03编写钩子、队列任务或自定义端点
  4. 04开发 Payload 插件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jbmanllr/agent-skills --skill payload

安装后在 Claude Code 中输入/payload确认已加载。

详细介绍

Payload CMS 应用开发

Payload 是基于 Next.js 的原生 CMS,采用 TypeScript 优先架构,提供管理面板、数据库管理、REST/GraphQL API、身份验证和文件存储。

安装说明

这是面向 AI 编码 Agent 的 Payload 技能独立版本。

Claude Code 用户:

  • 建议安装官方 Payload Marketplace 插件,其中包含此技能、MCP 工具和其他功能
  • 仅当你不想使用完整插件时,才安装此独立技能

其他 AI Agent(Cursor、OpenCode、GitHub Copilot 等):

  • 直接安装此技能:
    npx skills add noklip-io/agent-skills --skill payload
    

快速参考

任务解决方案详情
自动生成 slugslugField()FIELDS.md#slug-field-helper
按用户限制内容使用查询的访问控制ACCESS-CONTROL.md#row-level-security-with-complex-queries
本地 API 用户操作user + overrideAccess: falseQUERIES.md#access-control-in-local-api
草稿/发布工作流versions: { drafts: true }COLLECTIONS.md#versioning--drafts
计算字段virtual: true 搭配 afterReadFIELDS.md#virtual-fields
条件字段admin.conditionFIELDS.md#conditional-fields
自定义字段验证validate 函数FIELDS.md#validation
筛选关联列表字段上的 filterOptionsFIELDS.md#relationship
选择特定字段select 参数QUERIES.md#field-selection
自动设置作者/日期beforeChange 钩子HOOKS.md#collection-hooks
防止钩子循环检查 req.contextHOOKS.md#context
级联删除beforeDelete 钩子HOOKS.md#collection-hooks
地理空间查询point 字段搭配 near/withinFIELDS.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: trueADVANCED.md#localization
创建插件(options) => (config) => ConfigPLUGIN-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

快速入门

npx create-payload-app@latest my-app
cd my-app
pnpm dev
最小配置
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,
  }),
})

核心模式

基本集合
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。

常见字段
// 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' }

所有字段类型(array、blocks、point、join、virtual、conditional 等),请参阅 FIELDS.md。

Hook 示例
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' }],
}

所有 Hook 模式,请参阅 HOOKS.md。有关访问控制,请参阅 ACCESS-CONTROL.md。

类型安全的访问控制
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 },
  }
}
查询示例
// 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" }

所有查询运算符以及 REST/GraphQL 示例,请参阅 QUERIES.md。

获取 Payload 实例
// 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>
}

安全隐患

1. Local API 访问控制(严重)

默认情况下,Local API 操作会绕过所有访问控制,即使传入了用户也是如此。

// ❌ 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
})

各选项的适用场景:

  • overrideAccess: true(默认)- 可信的服务端操作(定时任务、系统任务)
  • overrideAccess: false - 代表用户执行操作时(API 路由、Webhook)

请参阅 QUERIES.md#access-control-in-local-api。

2. Hook 中的事务失败

Hook 中的嵌套操作若未传入 req,会破坏事务的原子性。

// ❌ 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
      })
    },
  ]
}

请参阅 ADAPTERS.md#threading-req-through-operations。

3. 无限 Hook 循环

Hook 触发的操作又触发相同的 Hook,会造成无限循环。

// ❌ 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,
      })
    },
  ]
}

请参阅 HOOKS.md#context。

项目结构

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

类型生成

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

// Usage
import type { Post, User } from '@/payload-types'

参考文档

  • FIELDS.md - 所有字段类型、验证和管理界面选项
  • FIELD-TYPE-GUARDS.md - 用于运行时字段类型检查和缩窄的类型守卫
  • COLLECTIONS.md - 集合配置、身份验证、上传、草稿、实时预览
  • HOOKS.md - 集合 Hook、字段 Hook、上下文模式
  • ACCESS-CONTROL.md - 集合、字段、全局访问控制,RBAC,多租户
  • ACCESS-CONTROL-ADVANCED.md - 基于上下文、时间和订阅的访问控制,工厂函数,模板
  • QUERIES.md - 查询运算符、Local/REST/GraphQL API
  • ENDPOINTS.md - 自定义 API 端点:身份验证、辅助函数、请求/响应模式
  • ADAPTERS.md - 数据库、存储、电子邮件适配器,事务
  • ADVANCED.md - 身份验证、作业、端点、组件、插件、本地化
  • PLUGIN-DEVELOPMENT.md - 插件架构、单体仓库结构、模式、最佳实践

资源

评论

支持 Markdown