この記事は newmo Advent Calendar 2025 15日目の記事です。
はじめに
newmoでは2025年6月から求人サービス「newジョブ」の開発を進めており、その中でHeadless CMSとしてPayloadCMSを採用しました。
本記事では、約半年の本番運用を通じて得た知見を共有します。

なぜHeadless CMSが必要だったか
jinzaiチームは少人数で構成されており、以下のような複数の用途に対応する必要がありました。
- 求人情報の管理・配信
- SEO対策のための構造化データ管理
- 運用チームによるコンテンツ編集
- 将来的な機能拡張への対応
既存のSaaSでは要件を満たしきれず、カスタマイズ性の高いHeadless CMSが必要でした。
PayloadCMSとは
PayloadCMSは、TypeScriptで書かれたオープンソースのHeadless CMSです。


https://payloadcms.com/use-cases/headless-cms
主な特徴
| 特徴 | 説明 |
|---|---|
| TypeScript Native | スキーマ定義から型が自動生成される |
| Next.js統合 | v3.0からApp Routerにネイティブ対応 |
| セルフホスト可能 | 自社インフラでの運用が可能 |
| 柔軟なDB対応 | PostgreSQL、MongoDB、SQLiteに対応 |
| コード駆動 | GUIではなくコードでスキーマを定義 |
本比較はnewジョブの要件に基づく用途適合の観点です。他用途では各CMSの強みが発揮され得ます。
Headless CMSの中での位置づけ
PayloadCMSは「コードでCMSを定義する」アプローチを取っており、開発者にとっての自由度が高いのが特徴です。
2024-2025年の動向
- 2024年11月: PayloadCMS 3.0リリース - Next.js App Router統合
- 2025年6月: Figmaによる買収発表
- 現在: v3.64.0が最新(2025年12月時点)
買収後もオープンソースとして開発が継続されており、Figmaのデザインツールとの連携強化が期待されています。
参考:
- PayloadCMS 公式サイト
- Payload 3.0: The first CMS that installs directly into any Next.js app
- Payload is joining Figma!
PayloadCMS選定の理由
複数のHeadless CMSを比較検討した結果、PayloadCMSを選定しました。
比較検討したCMS
| CMS | 特徴 | 今回の用途における結論 |
|---|---|---|
| PayloadCMS | TypeScript native、Next.js統合 | 今回の用途で最適と判断(型安全性と拡張性) |
| Strapi | プラグインエコシステム充実 | 今回の要件に対する適合度は相対的に低い |
| Sanity | リアルタイム編集、GROQ | 学習コストが今回の条件では高めと判断 |
| microCMS | 日本製、シンプル | 将来の拡張要件への適合度が相対的に低い |
本比較はnewジョブの要件に基づく用途適合の観点です。他用途では各CMSの強みが発揮され得ます。
決め手となったポイント
1. TypeScript Native
スキーマ定義からフロントエンドまで一貫した型安全性を確保できます。コレクション定義がそのまま型定義になるため、型定義の二重管理が不要です。
2. Next.js App Router統合
PayloadCMS 3.0からNext.js App Routerにネイティブ対応しています。CMSがNext.jsアプリとして動作するため、カスタムページの追加が容易です。
3. 拡張性
CMSを越えて「管理画面基盤」として使える拡張性が決め手に。hookによる処理拡張や管理画面のカスタムが素直に実装でき、実運用で効きました。特に次の2点は生産性と将来の対応力に直結します。
- REST/GraphQL APIの自動生成: スキーマ変更が型とAPIに自動伝播し、SSGビルドや外部連携の変更コストを抑制
- 認証機能の提供(管理画面とAPI): ロールベースの認可やAPIキー運用を内製せずに利用でき、セキュリティと開発速度を両立
実際に各CMSをinitializeして触った結果、PayloadCMSが最も柔軟に要件をカバーできると判断しました。
Figma買収とセルフホスト移行
予期せぬ方針転換
元々、運用負荷を下げるためPayload Cloud(PayloadCMS公式のホスティングサービス)を利用する予定でした。
しかし、リリース直前の2025年6月、FigmaによるPayloadCMS買収が発表され、Payload Cloudが一時的にクローズされることになりました。
Cloud Runへの移行
急遽、Google CloudのCloud Runにセルフホストする方針に転換しました。
- 移行作業期間: 約1週間
- タイミング: リリース前だったことが幸い
- 結果: 問題なく稼働開始
「どちらでも動く」という安心感
この経験から得られた重要な学びは、PayloadCMSが「どちらでも動く」設計になっているということです。
// payload.config.ts - 本番環境での設定 db: postgresAdapter({ pool: { connectionString: env.DATABASE_URI, }, push: false, // セルフホスト時は自動マイグレーション無効化 }),
Payload CloudでもセルフホストでもPayloadCMS自体のコードは同じため、ホスティング先の変更に柔軟に対応できました。
monorepo統合の実際
newmoではpnpm workspaceを使った大規模monorepoでサービスを開発しています。PayloadCMSプロジェクトもこのmonorepoに統合しました。
共有できたもの
1. DBマイグレーション(Atlas)
newmoではAtlasを使ってデータベーススキーマを管理しています。PayloadCMSのスキーマもこの仕組みに統合しました。
# PayloadCMSの設定からSQLスキーマを抽出 pnpm run --filter @newmo-app/payloadcms extract-schema # 出力先: server/component/newjob/db/postgres/schema.sql
extract-schemaコマンドでは、以下の処理を行っています。
// 入力: コレクション定義 // collections/Tags.ts export const Tags: CollectionConfig = { slug: "tags", fields: [ { name: "name", type: "text", required: true }, { name: "order", type: "number", defaultValue: 0 }, { name: "isPublished", type: "checkbox", defaultValue: true }, { name: "color", type: "select", options: [ { label: "赤", value: "red" }, { label: "青", value: "blue" }, { label: "緑", value: "green" }, ], }, ], };
// scripts/extract-payload-schema.ts(疑似コード) async function main() { // 1. PayloadCMSのDrizzleスキーマを生成 await exec("npx payload generate:db-schema"); // 2. Drizzle KitでSQLを生成 await exec("npx drizzle-kit generate"); // 3. Atlasが管理するディレクトリに出力 await consolidateSQL("server/component/newjob/db/postgres/schema.sql"); }
// 出力: schema.sql CREATE TYPE "public"."enum_tags_color" AS ENUM('red', 'blue', 'green'); CREATE TABLE "tags" ( "id" serial PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "order" numeric DEFAULT '0', "is_published" boolean DEFAULT true, "color" "enum_tags_color", "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL );
これにより、PayloadCMSのコレクション定義を変更するだけで、Atlasのマイグレーションフローに自動的に乗せることができます。
参考: Atlas - Database Schema as Code
2. デザインシステム
monorepo内の共通デザインシステム(Panda CSS)をそのまま利用できます。
// payload-cms/panda.config.ts import { defineConfig } from "@pandacss/dev"; import { newmoPreset } from "@newmo-app/panda-preset"; export default defineConfig({ presets: [newmoPreset], // ... });
3. CI設定
lint、test、型チェックなどの設定をmonorepo全体で共有しています。
# .github/workflows/ci.yml(抜粋) - name: Lint run: pnpm run lint - name: Type Check run: pnpm run typecheck
新しくPayloadCMSプロジェクトを追加しても、細かいCI設定を一から書く必要がありません。
参考: Renovate + pnpm catalog でmonorepoの依存関係を効率管理
4. デプロイの独立性
社内の共有インフラに乗りながらも、アプリケーションのデプロイは独立して行えます。PayloadCMSのapp単体でデプロイ可能で、他のアプリに影響を与えません。
コレクション設計の学び
PayloadCMSを使い始めて最初に躓いたのが、コレクション(画面)とDBテーブル(ドメイン)の設計の分離でした。
コレクション ≠ DBテーブル
PayloadCMSのコレクションは管理画面と密接に結びついています。一方、ドメイン設計の観点からはDBテーブルを正規化したい場面があります。

設計方針:拡張にオープン、画面は分離
私たちが採用した方針は以下の通りです。
- ドメイン設計を優先してDBテーブル(コレクション)を設計
- 管理画面の利便性は別途カスタマイズで対応
- 拡張性を確保するため、将来の要件も見据えた構造に
hookの活用例
例えば、求人の住所情報が更新されたら自動的に緯度経度を取得するhookを実装しています。
// collections/JobListings.ts(疑似コード) const JobListings: CollectionConfig = { slug: "job-listings", hooks: { beforeChange: [ async ({ data, req }) => { // 住所IDから位置情報を同期 if (data.addressId) { const address = await req.payload.findByID({ collection: "job-listings-addresses", id: data.addressId, }); if (address) { data.location = { type: "Point", coordinates: [address.longitude, address.latitude], // 緯度経度情報 }; } } return data; }, ], }, // ... };
このように、コレクション間の連携もhookで柔軟に実現できます。
参考: PayloadCMS Hooks
余談ですが、DBにPostgreSQLを採用している場合に上記のPoint型を利用すると、自動で利用できるAPIへのリクエストでそのまま近傍・範囲・インターセクト検索クエリが使える為、位置情報を扱うユースケースにおいてとても便利に利用することが出来ます。
参考: Point Field | Documentation | Payload
まとめ
半年運用しての所感
PayloadCMSを半年間本番運用してきて、特に困った点はありませんでした。
- v3からのスタート: 最初からv3.0を使っているため、v2からの移行問題なし
- アップデート追従: 定期的なアップデートも問題なく適用
- 安定性: 本番環境で大きな障害なし
PayloadCMSを「CMS」ではなく「管理画面基盤」として捉える
PayloadCMSの真価は、単なるCMSではなく、Next.jsベースの管理画面基盤として活用できる点にあると考えています。
- コンテンツ管理以外の管理機能も追加可能
- カスタムページ、カスタムAPIの追加が容易
- 認証・認可の仕組みをそのまま流用
少人数チームで複数の用途に対応する必要がある場合、PayloadCMSは有力な選択肢です。
最後に
Payload を選んだ理由をひと言でまとめるなら、「変更に強いから」。要件が動く前提で、型、API、UI を同時に追従させられる。少人数の開発チームにとってこの“追従の速さ”は安全装置でした。
これからも「速く、雑に」ではなく「速く、正しく」を実現していきたいと思います。
newmo Engineering Advent Calendar 2025: written by Claude Code(opus 4.5) w/ @yui_tang
参考リンク
- PayloadCMS 公式サイト
- PayloadCMS 公式ドキュメント
- The fastest growing open-source headless CMS | Payload
- Payload 3.0: The first CMS that installs directly into any Next.js app
- Payload is joining Figma!
- Payload Collections
- Payload Hooks
- Atlas - Database Schema as Code
- pnpm workspaceを使った大規模monorepoでサービス
- Renovate + pnpm catalog でmonorepoの依存関係を効率管理
- newmo Tech Blog