mcpField
P
payload

payload

github.com/jbmanllr開発149 回インストール39 週間前に更新
リポジトリ

payload について

Payload CMS アプリを開発する AI コーディングエージェント向けのスキルです。Next.js ネイティブな CMS の構築に役立つ設定、クエリ、アクセス制御、フック、プラグインの情報をまとめています。

主な機能

  1. 01コレクションとフィールドの設定パターン
  2. 02クエリとアクセス制御のガイド
  3. 03フックとトランザクションの実践方法
  4. 04API エンドポイントとアダプターのリファレンス
  5. 05プラグインとローカライズの開発

活用シーン

  1. 01CMS のコレクション、フィールド、下書きの設定
  2. 02ユーザー操作に対するアクセス制御の実装
  3. 03フック、ジョブタスク、カスタムエンドポイントの実装
  4. 04Payload プラグインの開発

インストール

コマンドをコピーしてターミナルで実行します。

$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 コーディングエージェント向けの Payload スキルの単独バージョンです。

Claude Code ユーザー向け:

  • このスキルに加え、MCP ツールやその他の機能を含む公式の Payload Marketplace プラグインを優先してインストールしてください
  • フルプラグインを使用しない場合にのみ、この単独スキルをインストールしてください

その他の AI エージェント(Cursor、OpenCode、GitHub Copilot など)向け:

  • このスキルを直接インストールしてください:
    npx skills add noklip-io/agent-skills --skill payload
    

クイックリファレンス

タスク解決策詳細
スラッグを自動生成するslugField()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
計算フィールドafterRead と virtual: trueFIELDS.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.context のチェックHOOKS.md#context
カスケード削除beforeDelete フックHOOKS.md#collection-hooks
地理空間クエリnear/within を使う point フィールド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: 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 を参照してください。

フックの例
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 を参照してください。

型安全なアクセス制御
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(デフォルト)- 信頼できるサーバーサイドの操作(cron ジョブ、システムタスク)
  • overrideAccess: false - ユーザーに代わって操作する場合(API ルート、Webhook)

QUERIES.md#access-control-in-local-api を参照してください。

2. フックでのトランザクション失敗

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. 無限フックループ

フックが、そのフックを再び実行する操作を呼び出すと、無限ループが発生します。

// ❌ 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 - コレクションフック、フィールドフック、コンテキストパターン
  • ACCESS-CONTROL.md - コレクション、フィールド、グローバルのアクセス制御、RBAC、マルチテナント
  • ACCESS-CONTROL-ADVANCED.md - コンテキスト、時間、サブスクリプションに基づくアクセス制御、ファクトリー関数、テンプレート
  • QUERIES.md - クエリオペレーター、Local/REST/GraphQL API
  • ENDPOINTS.md - カスタム API エンドポイント:認証、ヘルパー、リクエスト/レスポンスパターン
  • ADAPTERS.md - データベース、ストレージ、メールのアダプター、トランザクション
  • ADVANCED.md - 認証、ジョブ、エンドポイント、コンポーネント、プラグイン、ローカライズ
  • PLUGIN-DEVELOPMENT.md - プラグインアーキテクチャ、モノレポ構成、パターン、ベストプラクティス

リソース

コメント