OSSのヘッドレスCMS比較:Strapi vs Directus vs Payload CMS でAPIファーストなコンテンツ管理を実現する
オープンソースラボ編集部 ・ 2026年6月14日
OSSのヘッドレスCMS比較:Strapi vs Directus vs Payload CMS でAPIファーストなコンテンツ管理を実現する
Contentful(月$300〜)・Sanity(月$99〜)・DatoCMS(月$99〜)に対して、Strapi(最もポピュラーなOSSヘッドレスCMS)・Directus(データベースラッパー型)・Payload CMS(TypeScript特化・Next.js組み込み可能)はOSSのヘッドレスCMSです。
ヘッドレスCMSが必要な場面
- Next.js/Nuxt.js との統合: フロントエンドをReact/Vueで書き、コンテンツ管理を分離する
- マルチチャネル配信: Webサイト・モバイルアプリ・デジタルサイネージを同一APIから配信
- 非開発者による更新: ライター・マーケターが管理UIでコンテンツを更新・公開管理
- ロールベースアクセス: 編集者・承認者・管理者で異なる権限を設定
主要ツールの概要
Strapi
2015年に公開されたNode.js製のOSSヘッドレスCMSです。GitHubスター63k+。OSSヘッドレスCMSで最多GitHub Starsを持ち、カスタムフィールドタイプ・REST API & GraphQL・ロールベースアクセス・プラグインシステム・マルチ言語対応・メディアライブラリを提供します。TypeScript対応・PostgreSQL/MySQL/SQLite対応。
# Strapiプロジェクトを作成
npx create-strapi-app@latest my-cms --dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_db --dbusername=strapi --dbpassword=strapi_password
cd my-cms
npm run develop
# 管理パネル: http://localhost:1337/admin
# API: http://localhost:1337/api/articles
// Strapi でカスタムコンテンツタイプを定義
// src/api/article/content-types/article/schema.json
const articleSchema = {
kind: "collectionType",
collectionName: "articles",
info: {
singularName: "article",
pluralName: "articles",
displayName: "Article",
},
options: { draftAndPublish: true },
attributes: {
title: { type: "string", required: true, minLength: 5 },
slug: { type: "uid", targetField: "title", required: true },
content: { type: "richtext" },
excerpt: { type: "text", maxLength: 300 },
cover: { type: "media", multiple: false, allowedTypes: ["images"] },
category: { type: "relation", relation: "manyToOne", target: "api::category.category" },
tags: { type: "relation", relation: "manyToMany", target: "api::tag.tag" },
seo: {
type: "component",
repeatable: false,
component: "shared.seo",
},
publishedAt: { type: "datetime" },
},
};
// Next.js App Router からStrapiのREST APIを呼び出す
// lib/strapi.ts
const STRAPI_URL = process.env.STRAPI_URL || "http://localhost:1337";
const STRAPI_TOKEN = process.env.STRAPI_TOKEN; // API Token
async function strapi<T>(path: string, params?: Record<string, string>): Promise<T> {
const url = new URL(`/api${path}`, STRAPI_URL);
if (params) {
Object.entries(params).forEach(([k, v]) => url.searchParams.append(k, v));
}
const res = await fetch(url, {
headers: { Authorization: `Bearer ${STRAPI_TOKEN}` },
next: { revalidate: 1800 }, // ISR: 30分キャッシュ
});
if (!res.ok) throw new Error(`Strapi API error: ${res.status}`);
return res.json();
}
// 記事一覧を取得
export async function getArticles(page = 1, pageSize = 10) {
return strapi("/articles", {
"populate": "cover,category,tags",
"pagination[page]": String(page),
"pagination[pageSize]": String(pageSize),
"sort": "publishedAt:desc",
"filters[publishedAt][$notNull]": "true",
});
}
// スラッグで記事を取得
export async function getArticleBySlug(slug: string) {
return strapi("/articles", {
"filters[slug][$eq]": slug,
"populate": "cover,category,tags,seo",
});
}
// Next.js App Router での使用(Server Component)
// app/blog/[slug]/page.tsx
export default async function ArticlePage({ params }: { params: { slug: string } }) {
const data = await getArticleBySlug(params.slug);
const article = data.data[0];
if (!article) notFound();
return <ArticleDetail article={article} />;
}
export async function generateStaticParams() {
const data = await getArticles(1, 100);
return data.data.map((article: any) => ({ slug: article.attributes.slug }));
}
Directus
2004年から開発されているTypeScript製のデータプラットフォームです。GitHubスター28k+。既存のSQLデータベースを直接ラップしてREST/GraphQL APIとWebUIを自動生成するアーキテクチャが独自で、Contentfulやdatocmsのような専用CMSというよりは「データベースに管理UIを被せるツール」です。PostgreSQL・MySQL・MS SQL・OracleのテーブルをそのままCMSとして扱えます。
# docker-compose.yml - Directus + PostgreSQL
version: '3.8'
services:
directus:
image: directus/directus:latest
ports:
- "8055:8055"
environment:
SECRET: "your-secret-key-here"
DB_CLIENT: "pg"
DB_HOST: "database"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
ADMIN_EMAIL: "admin@yourcompany.com"
ADMIN_PASSWORD: "admin-password-here"
PUBLIC_URL: "https://cms.yourcompany.com"
# Storage(ファイルアップロード先)
STORAGE_LOCATIONS: "local"
STORAGE_LOCAL_ROOT: "./uploads"
# キャッシュ
CACHE_ENABLED: "true"
CACHE_STORE: "redis"
REDIS: "redis://redis:6379"
depends_on: [database, redis]
volumes:
- directus_uploads:/directus/uploads
database:
image: postgres:15-alpine
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_password
volumes:
- db_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
directus_uploads:
db_data:
// Directus JavaScript SDKを使って記事を取得
import { createDirectus, rest, readItems, authentication } from "@directus/sdk";
const directus = createDirectus("https://cms.yourcompany.com")
.with(authentication("static", { token: process.env.DIRECTUS_TOKEN }))
.with(rest());
// 記事一覧を取得(フィールド指定・フィルタ・ソート)
const articles = await directus.request(
readItems("articles", {
fields: ["id", "title", "slug", "content", "date_published", { category: ["name"] }],
filter: { status: { _eq: "published" } },
sort: ["-date_published"],
limit: 10,
offset: 0,
})
);
// 特定のスラッグで記事を取得
const [article] = await directus.request(
readItems("articles", {
filter: { slug: { _eq: "my-article-slug" } },
fields: ["*", { category: ["*"] }, { tags: [{ tags_id: ["name"] }] }],
})
);
Payload CMS
2022年に公開されたTypeScript製のOSSヘッドレスCMSです。GitHubスター31k+。Next.js App Routerとの統合が最も緊密で、同一Next.jsプロジェクト内にCMSを組み込む(ローカル管理パネル)ことが可能です。TypeScriptファーストの設計で型安全なコンテンツスキーマ定義・MongoDB/PostgreSQL対応・強力なアクセス制御(関数ベース)が特徴です。
// payload.config.ts - Payload CMSの設定
import { buildConfig } from "payload/config";
import { mongooseAdapter } from "@payloadcms/db-mongodb";
import { lexicalEditor } from "@payloadcms/richtext-lexical";
import { Articles } from "./collections/Articles";
import { Users } from "./collections/Users";
export default buildConfig({
editor: lexicalEditor({}),
collections: [Articles, Users],
secret: process.env.PAYLOAD_SECRET!,
typescript: { outputFile: resolve(__dirname, "payload-types.ts") },
db: mongooseAdapter({ url: process.env.MONGODB_URI! }),
admin: {
user: "users",
bundler: webpackBundler(),
},
});
// collections/Articles.ts - 型安全なコンテンツスキーマ
import type { CollectionConfig } from "payload/types";
export const Articles: CollectionConfig = {
slug: "articles",
admin: { useAsTitle: "title" },
access: {
read: () => true, // 公開読み取り可能
create: ({ req }) => Boolean(req.user), // ログイン必須
update: ({ req }) => req.user?.role === "editor", // editor以上のみ
delete: ({ req }) => req.user?.role === "admin", // adminのみ削除
},
hooks: {
beforeChange: [
({ data }) => ({
...data,
updatedAt: new Date().toISOString(),
}),
],
},
fields: [
{ name: "title", type: "text", required: true },
{ name: "slug", type: "text", unique: true },
{ name: "content", type: "richText" },
{ name: "status", type: "select", options: ["draft", "published"], defaultValue: "draft" },
{ name: "category", type: "relationship", relationTo: "categories" },
],
};
機能比較表
| 比較項目 | Strapi | Directus | Payload CMS |
|---|---|---|---|
| Next.js統合 | REST/GraphQL | REST/GraphQL | ✅(組み込み可能) |
| TypeScript | ✅(v4〜) | ✅ | ✅(設計から) |
| GraphQL | ✅(プラグイン) | ✅(組み込み) | ✅ |
| DB既存テーブル統合 | ❌ | ✅(強み) | ❌ |
| 言語 | Node.js/TS | TypeScript | TypeScript |
| GitHub Stars | 63k+ | 28k+ | 31k+ |
Strapiのコンテンツ更新をSlackに通知するにはCommunicationカテゴリ/categories/communicationのSlack Webhookと連携できます。CMSのファイル(画像・動画)ストレージにはDevOpsカテゴリ/categories/devopsのMinIOをS3互換で設定できます。
FAQ
Q. WordPressからStrapiに移行するメリット・デメリットは?
A. メリット: ①フロントエンドの自由度(Next.js/Nuxt.jsでモダンなUIを作れる)②パフォーマンス(静的サイト生成でWordPressの10〜100倍高速)③セキュリティ(PHP脆弱性・プラグインの穴が激減)④APIファーストでモバイルアプリにも同じコンテンツを配信。デメリット: ①CMSとフロントを別々に管理する必要(複雑さが増す)②WordPressの豊富なプラグインエコシステムがない③SEO設定・サイトマップ生成を自分で実装③移行コスト(既存コンテンツの変換・ライターへの再教育)。WordPressをコンテンツ管理だけに使いつつフロントをNext.jsにするHeadless WordPress(WPGraphQL)という選択肢もあります。
Q. Strapiの管理パネルを本番でセキュアに公開するには?
A. ①管理パネルを別サブドメイン+IP制限: admin.yourcompany.comを社内IPのみ許可②環境変数でAdmin URLを変更: ADMIN_PATH=/secret-cms-path(デフォルトの/adminを変更)③MFA必須化: Strapi EnterpriseのSSO/SAML統合でMFAを強制④APIトークンにはロール設定: 読み取り専用・フルアクセスを分けて最小権限で発行⑤定期的なバックアップ: pg_dumpまたはmysqldumpをcronで実行してS3に保存。パブリックなインターネットに管理パネルをそのまま公開するのは避け、VPN越しかIP制限を必ずかけてください。
Q. Payload CMSをNext.js App Routerと同一プロセスで動かすメリットは?
A. メリット: ①セルフホストが1つのサービスで完結(CMSとWebアプリを別々にデプロイしなくていい)②ローカルAPI(payload.find())がHTTP経由より高速(ネットワークレイテンシゼロ)③型安全なデータ取得(payload-types.tsからコンテンツ型が自動生成される)④Vercel 1クリックデプロイが可能。注意: Next.js App RouterはNode.jsランタイムとEdgeランタイムが混在するため、Payloadはnext.config.jsのexperimental.serverComponentsExternalPackagesにpayloadを追加する設定が必要。
Q. DirectusはWordPressの置き換えではなくどんな用途に向いていますか?
A. Directusは「既存のSQLデータベースに管理UIを生やすツール」として独自の強みを持ちます。適した用途: ①既存DBにCMS管理UIが欲しい: PostgreSQLのテーブルをそのままWebUIで管理できる②データダッシュボード: 社内データをノーコードでブラウズ・検索・フィルタ③内部ツール: Retool/Budibase代替として自社データの管理画面を作る④ウェアハウスの可視化: BigQuery/SnowflakeにDirectusを被せてノンエンジニアがデータを確認。Contentful/StrapiのようなCMS機能(ドラフト管理・多言語・ワークフロー承認)よりも「任意のデータを管理する」ユースケースが本命です。
まとめ
| ユースケース | 推奨ツール |
|---|---|
| Contentful/Sanity代替・マルチサイト | Strapi |
| 既存DBに管理UIを追加・内部ツール | Directus |
| Next.js組み込み・TypeScript特化 | Payload CMS |