mcpField
P
payload

payload

github.com/payloadcms開発1706.9k 回インストール16 週間前に更新
リポジトリ

payload について

payloadは、Next.jsネイティブのCMSであるPayloadの開発を支援する、TypeScript優先のスキルです。コンテンツ設計、アクセス制御、クエリ、フック、プラグインの実装パターンを扱います。

主な機能

  1. 01コレクションとフィールドの設定
  2. 02ドラフト、バージョン、ローカライズの管理
  3. 03アクセス制御とクエリの設定
  4. 04フックとカスタムAPIルートの作成
  5. 05プラグイン開発と型生成

活用シーン

  1. 01ドラフト・公開フローを備えたCMSの構築
  2. 02ユーザー権限に応じたコンテンツ制限
  3. 03計算フィールドとフックの実装
  4. 04プラグインによるPayloadの拡張

インストール

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

$npx skills add https://github.com/payloadcms/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 アプリケーション開発

Payload は Next.js ネイティブの CMS で、TypeScript ファーストのアーキテクチャを採用しており、管理パネル、データベース管理、REST/GraphQL API、認証、ファイルストレージを提供します。

クイックリファレンス

タスク解決策詳細
スラッグを自動生成する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
計算フィールド値を返すフィールドレベルの hooks.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,
  }),
})

重要なパターン

デフォルトと慣例

明確な理由がない限り、コンテンツをモデル化する際は次のデフォルトを適用してください。

  • デフォルトで下書き/バージョンを有効にする: versions: { drafts: true }。これは、あらゆるコンテンツコレクションで推奨される開始点です。_status フィールド(draft/published/changed)が自動的に挿入されます。**独自の status フィールドは追加しないでください。**重複するためです。公開/下書きのライフサイクルがないコレクション(内部の結合テーブル、設定など)に限り、バージョンを省略してください。
  • すべてのスラッグには、独自に { name: 'slug', type: 'text', unique: true } を作成するのではなく、slugField() を使用してください。タイトルからスラッグを自動生成し、再生成の切り替えを追加して、一意性とインデックス作成を処理します。デフォルトでは title フィールドから生成します。コレクションに title がない場合は、ソースフィールドを渡してください:slugField({ useAsSlug: 'name' })。
  • position: 'sidebar' は、ひと目で確認できる短いフィールド(ステータス、カテゴリー、作成者、公開日)に使用します。使いやすさに横幅が必要な長いフィールド(説明、リッチテキストコンテンツ、長文テキスト)には使用しないでください。これらはメインのドキュメント領域に配置します。
基本的なコレクション
import type { CollectionConfig } from 'payload'
import { slugField } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  admin: {
    useAsTitle: 'title',
    // _status (from versions.drafts) shows the draft/published state — no custom status field needed
    defaultColumns: ['title', 'author', '_status', 'createdAt'],
  },
  versions: {
    drafts: true,
  },
  fields: [
    { name: 'title', type: 'text', required: true },
    slugField(), // auto-generates from `title`, unique + indexed, sidebar position
    { name: 'content', type: 'richText' }, // long field — stays in the main area, not the sidebar
    // short, at-a-glance field — good sidebar candidate
    { name: 'author', type: 'relationship', relationTo: 'users', admin: { position: 'sidebar' } },
  ],
  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 }

// Slug — use the helper instead of a hand-rolled text field
slugField()

// Select (for genuine taxonomy — NOT publish state; use versions.drafts + _status for that)
{ name: 'category', type: 'select', options: ['news', 'tutorial', 'opinion'] }

// Upload
{ name: 'image', type: 'upload', relationTo: 'media' }

すべてのフィールドタイプ(array、blocks、point、join、virtual、conditional など)については、FIELDS.md を参照してください。

フックの例

フックには 2 つのレベルがあり、相互に置き換えることはできません。コレクションフックは { doc, data, req, operation, ... } を受け取り、ドキュメント全体に作用します。フィールドフックは個々のフィールドの hooks オブジェクト内にあり、{ value, siblingData, ... } を受け取り、そのフィールドの新しい値を返します。計算フィールド/仮想フィールド、フィールドごとのフォーマッター、フィールドごとのアクセスマスキングにはフィールドフックを使用します。フィールドをまたぐビジネスロジックにはコレクションフックを使用します。

// Collection-level: business logic across the document
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' }],
}

// Field-level: compute / format a single field's value (virtual fields use this)
export const Users: CollectionConfig = {
  slug: 'users',
  fields: [
    { name: 'firstName', type: 'text' },
    { name: 'lastName', type: 'text' },
    {
      name: 'fullName',
      type: 'text',
      virtual: true,
      hooks: {
        afterRead: [({ siblingData }) => `${siblingData.firstName} ${siblingData.lastName}`],
      },
    },
  ],
}

「フィールドを計算する」または「フックでフィールドの値を設定する」よう求められた場合は、そのフィールドにフィールドレベルのフックを使用してください。doc を変更するコレクションレベルの afterRead は使用しないでください。

すべてのフックパターンについては、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 は payload-types.ts を生成するため、手動で generate:types を実行する必要はほとんどありません。

  • 開発中: typescript.autoGenerate のデフォルト値は true なので、設定を変更するたびに開発サーバーが型を自動的に再生成します。開発サーバーの実行中に generate:types を手動で実行しないでください。重複した処理になります。
  • ビルド中: payload build は next build の実行前にインポートマップと型を生成します。どちらも古い状態にならないよう、next build を直接呼び出すよりこちらを優先してください。型生成をスキップするには --no-types を渡します。
  • 手動生成(payload generate:types)は、開発サーバーもビルドも実行されていない場合に限る最終手段です(例:単発のスクリプト、または payload build を実行しないステップの前に CI で実行する場合)。
// payload.config.ts
export default buildConfig({
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
    // autoGenerate defaults to true — types regenerate in dev automatically
  },
})

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

よくある注意点

  1. Local API はアクセス制御をバイパスします。overrideAccess: false を渡した場合を除きます
  2. ネストされた操作で req が不足すると、トランザクションのアトミック性が損なわれます
  3. フックのループ — フック内の操作によって同じフックが再実行されることがあります。req.context フラグを使用してください
  4. フィールドレベルのアクセス制御が返すのはブール値のみで、クエリ制約は指定できません
  5. リレーションシップの深さのデフォルトは 2 です。ID のみを取得する場合は depth: 0 を設定してください
  6. 下書きステータス — 下書きを有効にすると、_status フィールドが自動的に挿入されます
  7. 型は開発中(autoGenerate)および payload build の実行中に自動再生成されます。generate:types の手動実行は避けてください
  8. MongoDB のトランザクションにはレプリカセットの設定が必要です
  9. SQLite のトランザクションはデフォルトで無効です。transactionOptions: {} で有効にしてください
  10. Point フィールドは SQLite ではサポートされていません

ベストプラクティス

コンテンツモデリング
  • コンテンツコレクションでは、デフォルトで versions: { drafts: true } を有効にしてください。カスタムの status フィールドを追加するのではなく、自動挿入される _status フィールドを使用します
  • 独自に一意のテキストフィールドを作成するのではなく、スラッグには slugField() を使用してください
  • position: 'sidebar' は、ひと目で確認できる短いフィールド(ステータス、カテゴリー、作成者、日付)に使用し、長いフィールド(説明、リッチテキスト)はメイン領域に配置してください
セキュリティ
  • まず制限の厳しいアクセス制御を設定し、権限を段階的に追加してください
  • Local API に user を渡す場合は、overrideAccess: false を使用してください
  • フィールドレベルのアクセス制御が返すのはブール値のみで、クエリ制約は指定できません
  • クライアントから渡されたデータを決して信用しないでください
  • データベースへの参照を避けるため、ロールには saveToJWT: true を使用してください
パフォーマンス
  • 頻繁にクエリされるフィールドにインデックスを設定してください
  • 返されるフィールドを制限するには select を使用してください
  • 過剰なデータ取得を防ぐため、リレーションシップに maxDepth を設定してください
  • アクセス制御では、非同期処理よりクエリ制約を優先してください
  • コストの高い処理は req.context にキャッシュしてください
データ整合性
  • フック内のネストされた操作には必ず req を渡してください
  • 無限のフックループを防ぐため、コンテキストフラグを使用してください
  • MongoDB(レプリカセットが必要)と Postgres ではトランザクションを有効にしてください
  • データの形式を整えるには beforeValidate を使用してください
  • ビジネスロジックには beforeChange を使用してください
型安全性
  • 開発中(autoGenerate)および payload build に型を生成させてください。generate:types を手動で実行するのは、どちらも実行されていない場合に限ります
  • 生成された payload-types.ts から型をインポートしてください
  • ユーザーオブジェクトに型を指定してください:import type { User } from '@/payload-types'
  • 実行時の型チェックにはフィールド型ガードを使用してください
  • Payload の値(コレクション、フィールド、フック、アクセス関数、プラグインなど)を名前付き定数に抽出する場合は、対応する Payload の型(CollectionConfig、Field、CollectionBeforeChangeHook、Access、Plugin など)で注釈を付けるか、satisfies <Type> を使用してください。注釈がないと、type: 'text' のような文字列プロパティが string に拡大され、判別共用体(Field、CollectionConfig)を解決できなくなります。インラインリテラルではコンテキスト型付けによって自動的に型が適用されますが、抽出した定数には適用されません。
構成
  • コレクションは別々のファイルに分けてください
  • アクセス制御は access/ ディレクトリに分離してください
  • フックは hooks/ ディレクトリに分離してください
  • よく使うパターンには再利用可能なフィールドファクトリーを使用してください
  • 複雑なアクセス制御にはコメントで説明を加えてください

リファレンスドキュメント

  • 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 - プラグインアーキテクチャ、モノレポ構造、パターン、ベストプラクティス

リソース

コメント