mcpField
P
payload

payload

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

关于 payload

面向使用 Next.js 和 TypeScript 开发 Payload CMS 应用的 AI 编码 Agent,提供参考指南。内容涵盖集合、字段、访问控制、查询、钩子和插件开发模式。

主要功能

  1. 01集合与字段配置模式
  2. 02访问控制与查询指南
  3. 03钩子、事务与作业队列
  4. 04REST 和 GraphQL API 参考
  5. 05插件开发指南

适用场景

  1. 01构建或扩展 Payload CMS 应用
  2. 02配置访问规则与数据查询
  3. 03开发 Payload 插件和自定义端点

安装

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

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

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
reference/
ACCESS-CONTROL-ADVANCED.md
ACCESS-CONTROL.md
ADAPTERS.md
ADVANCED.md
COLLECTIONS.md
ENDPOINTS.md
FIELD-TYPE-GUARDS.md
FIELDS.md
HOOKS.md
PLUGIN-DEVELOPMENT.md
QUERIES.md

详细介绍

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