Payloadは、TypeScriptでデータ構造を定義すると、管理画面とAPIを生成するCMSです。今回はpostsコレクションを一つだけ作り、管理画面とREST APIの対応を確認します。

今回やること
- 公式の空テンプレートから起動する
title、slug、bodyを持つPostsを登録する- 管理画面でCRUDを試す
- draftを保存し、PreviewからJSONを確認する
Collectionは同じ構造を持つ文書のまとまりです。postsを定義すると、Payloadは管理画面の一覧・編集フォームと、/api/posts以下のREST APIを生成します。今回は独自APIや複数Collectionを追加しません。
プロジェクトを作る
Payloadの要件に合うNode.jsと、Dockerまたは選択するDBを準備します。
npx create-payload-app@latest -t blank
質問に従ってプロジェクト名とDBを選び、作成したディレクトリへ移動します。以降は生成されたREADME.mdにあるコマンドを優先してください。一般的には次で起動できます。
pnpm dev
ブラウザでhttp://localhost:3000/adminを開き、最初の管理者を作ります。
Postsコレクションを作る
src/collections/Posts.tsを作ります。
import type { CollectionConfig } from "payload";
export const Posts: CollectionConfig = {
slug: "posts",
admin: {
useAsTitle: "title",
preview: ({ id }) => `/api/posts/${id}?draft=true`,
},
access: {
read: ({ req }) =>
req.user ? true : { _status: { equals: "published" } },
create: ({ req }) => Boolean(req.user),
update: ({ req }) => Boolean(req.user),
delete: ({ req }) => Boolean(req.user),
},
versions: {
drafts: true,
},
fields: [
{ name: "title", type: "text", required: true },
{ name: "slug", type: "text", required: true, unique: true },
{ name: "body", type: "textarea", required: true },
],
};
payload.config.tsでPostsを読み込み、既存のcollectionsへ追加します。
import { Posts } from "./collections/Posts";
export default buildConfig({
// 生成済みのdb、editor、secretなどはそのまま残す
collections: [Users, Posts],
});
テンプレートによって既存Collection名やimport位置は異なります。buildConfig全体を上の短い例で置き換えず、Postsのimportと配列要素だけを加えてください。
設定の役割は次のとおりです。
| 設定 | 役割 |
|---|---|
slug: "posts" | 管理画面とAPIで使う名前 |
versions.drafts | 公開版とは別に下書きを保存 |
access | 未ログイン時は公開済みの読み取りだけ許可 |
admin.preview | 管理画面のPreviewボタンの移動先 |
CRUDを確認する
開発サーバーを再起動し、管理画面のPostsから次を試します。
title: 最初の記事、slug: first-post、本文を入力して作成する- 一覧で作った記事を参照する
- タイトルを更新して保存する
- 最後に記事を削除する
削除前に、REST APIでも一覧を確認できます。
curl http://localhost:3000/api/posts
Collectionのslugがpostsなので、Payloadが/api/postsを生成します。この設定では、ログインしていない人は公開済み文書だけを読め、作成・更新・削除とdraft取得はログインした管理者に限られます。
REST APIでは、一覧はGET /api/posts、1件はGET /api/posts/<ID>、更新はPATCH /api/posts/<ID>、削除はDELETE /api/posts/<ID>です。今回は認証情報をcurlへコピーせず、書き込み操作を管理画面で行います。
一覧応答は文書配列そのものではなく、docsとページ情報を含むオブジェクトです。フロントエンドから使う時はresult.docsを読み、件数が増えたらpageとlimitも扱います。
下書きをプレビューする
記事をもう一つ作り、Save Draftで下書き保存します。保存後に管理画面のPreviewを押すと、/api/posts/<ID>?draft=trueが開き、ログイン中の管理者として下書きJSONを確認できます。
このプレビューは表示ページではなく、draft取得の最小確認です。実際のサイトでは、推測できないpreview secretの検証、管理者認証、Next.jsのdraft modeを備えた専用ルートを作ります。
Previewを押す前に一度Save Draftを行います。まだ保存されていない新規文書には確定したIDがないため、プレビューURLも確定しません。
成功確認
- 管理画面のPostsに作成した文書が表示される
GET /api/postsで公開した文書が返る- 下書きのPreviewで
_status: "draft"の文書を確認できる - 更新内容が保存され、削除後は一覧から消える
よくあるつまずき
Postsが管理画面に出ない
Postsをexportしただけでは登録されません。payload.config.tsのcollections配列へ追加し、開発サーバーを再起動します。
Previewで公開版しか見えない
versions.drafts、URLのdraft=true、管理者としてログインしているかを確認します。公開APIへ認証なしでdraftを返してはいけません。
練習
publishedAtというdateフィールドを一つ追加してください。管理画面で値を保存し、REST APIのJSONにも同じ値が含まれることを確認します。
後片付け
練習用文書は管理画面から削除します。開発サーバーはCtrl + Cで停止します。DBコンテナをテンプレートが起動した場合は、生成されたREADMEの停止手順を使います。