AI

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" },
  ],
};

機能比較表

比較項目StrapiDirectusPayload CMS
Next.js統合REST/GraphQLREST/GraphQL✅(組み込み可能)
TypeScript✅(v4〜)✅(設計から)
GraphQL✅(プラグイン)✅(組み込み)
DB既存テーブル統合✅(強み)
言語Node.js/TSTypeScriptTypeScript
GitHub Stars63k+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.jsexperimental.serverComponentsExternalPackagespayloadを追加する設定が必要。

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

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。