AI

オープンソースヘッドレスCMS比較:Strapi vs Directus vs Payload CMS でAPIファーストCMSをセルフホストする

オープンソースラボ編集部2026年6月13日

オープンソースヘッドレスCMS比較:Strapi vs Directus vs Payload CMS でAPIファーストCMSをセルフホストする

WordPressの代替として「コンテンツAPIを提供するだけ」のヘッドレスCMSが主流になっています。Strapi・Directus・Payload CMSはオープンソースでセルフホストできるヘッドレスCMSです。Next.js・Nuxt・Astroなどのフロントエンドと組み合わせてコンテンツ管理システムを構築しましょう。

ヘッドレスCMSの特徴

従来のCMS(WordPress等)は「フロントエンド(テンプレート)」と「バックエンド(コンテンツ管理)」が密結合しています。ヘッドレスCMSはコンテンツをAPIで提供するだけで、フロントエンドは自由に選べます。

  • REST API / GraphQL API: コンテンツをJSONで返す
  • マルチチャンネル: Webサイト・モバイルアプリ・IoT等に同じコンテンツを配信
  • フロントエンドの自由: React・Vue・Next.js・Astroどれでも使える
  • コンテンツモデルの柔軟性: カスタムフィールドをUIで定義

主要ツールの概要

Strapi

Node.js製でGitHub Stars 65k+のオープンソースヘッドレスCMSです。コンテンツタイプビルダー・REST API・GraphQL API・ロールベースアクセス制御・マルチメディア管理を標準装備しています。

# Strapiプロジェクトの作成
npx create-strapi-app my-cms --quickstart
cd my-cms
npm run develop  # ローカル起動(http://localhost:1337/admin)

# Dockerで起動
docker run -d   -p 1337:1337   -v strapi_data:/opt/app/.tmp   -e DATABASE_CLIENT=sqlite   strapi/strapi
// Strapi v5 カスタムルートとコントローラー
// src/api/article/routes/article.ts
export default {
  routes: [
    {
      method: "GET",
      path: "/articles/featured",
      handler: "article.findFeatured",
    },
  ],
};

// src/api/article/controllers/article.ts
import { factories } from "@strapi/strapi";

export default factories.createCoreController(
  "api::article.article",
  ({ strapi }) => ({
    async findFeatured(ctx) {
      const articles = await strapi.entityService.findMany(
        "api::article.article",
        {
          filters: { featured: true },
          sort: { publishedAt: "desc" },
          limit: 10,
          populate: ["thumbnail", "author", "categories"],
        }
      );
      ctx.body = articles;
    },
  })
);
// Next.js App RouterからStrapiのAPIを呼び出す
interface Article {
  id: number;
  attributes: {
    title: string;
    content: string;
    publishedAt: string;
  };
}

async function getArticles(): Promise<Article[]> {
  const res = await fetch(
    `${process.env.STRAPI_URL}/api/articles?populate=*&sort=publishedAt:desc`,
    {
      headers: { Authorization: `Bearer ${process.env.STRAPI_API_TOKEN}` },
      next: { revalidate: 60 },
    }
  );
  const data = await res.json();
  return data.data;
}

Directus

直感的なUIとゼロ設定のデータプラットフォームです。SQLデータベースをそのままAPIとして公開でき、既存のDBにCMS機能を後付けする「データラッパー」として使えます。TypeScriptファーストで型安全なSDKを提供します。

# Directusをdocker-composeで起動
version: '3'
services:
  database:
    image: postgres:16
    environment:
      POSTGRES_DB: directus
      POSTGRES_USER: directus
      POSTGRES_PASSWORD: secret

  directus:
    image: directus/directus:11
    ports:
      - 8055:8055
    environment:
      SECRET: your-secret-key
      DB_CLIENT: pg
      DB_HOST: database
      DB_PORT: 5432
      DB_DATABASE: directus
      DB_USER: directus
      DB_PASSWORD: secret
      ADMIN_EMAIL: admin@example.com
      ADMIN_PASSWORD: admin-password
    depends_on:
      - database
// Directus JavaScript SDK
import { createDirectus, rest, authentication, readItems, createItem } from "@directus/sdk";

const client = createDirectus("http://localhost:8055")
  .with(authentication("json"))
  .with(rest());

await client.login("admin@example.com", "admin-password");

// コレクション(テーブル)からアイテムを取得
const articles = await client.request(
  readItems("articles", {
    fields: ["id", "title", "content", "published_at", { author: ["name", "avatar"] }],
    filter: { status: { _eq: "published" } },
    sort: ["-published_at"],
    limit: 10,
  })
);

// 新しいアイテムを作成
const newArticle = await client.request(
  createItem("articles", {
    title: "新しい記事",
    content: "記事の本文...",
    status: "draft",
  })
);

Payload CMS

TypeScript/Reactで書かれたコードファーストのヘッドレスCMSです。設定をコードで書き、TypeScriptの型安全性・React Hooksによるカスタムフィールドを活用できます。Next.jsとの統合が特に深いです。

// payload.config.ts
import { buildConfig } from "payload";
import { postgresAdapter } from "@payloadcms/db-postgres";
import { lexicalEditor } from "@payloadcms/richtext-lexical";

export default buildConfig({
  secret: process.env.PAYLOAD_SECRET,
  db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }),
  collections: [
    {
      slug: "articles",
      admin: { useAsTitle: "title" },
      fields: [
        { name: "title", type: "text", required: true },
        { name: "content", type: "richText", editor: lexicalEditor({}) },
        {
          name: "author",
          type: "relationship",
          relationTo: "users",
          required: true,
        },
        {
          name: "categories",
          type: "relationship",
          relationTo: "categories",
          hasMany: true,
        },
        { name: "publishedAt", type: "date" },
        {
          name: "status",
          type: "select",
          options: ["draft", "published"],
          defaultValue: "draft",
        },
      ],
      hooks: {
        beforeChange: [
          async ({ data, req }) => {
            if (data.status === "published" && !data.publishedAt) {
              data.publishedAt = new Date().toISOString();
            }
            return data;
          },
        ],
      },
    },
  ],
});

機能比較表

比較項目StrapiDirectusPayload CMS
コンテンツタイプビルダー(GUI)❌(コード)
REST API
GraphQL API
TypeScript✅(v5)✅ 完全
既存DB接続⚠️
Next.js統合✅ 深い
ロールベースアクセス
ファイル管理
マルチ言語対応(i18n)
Webhook
プラグイン/拡張
設定のコード化⚠️⚠️
ライセンスELv2BUSL 1.1MIT
GitHub Stars65k+29k+30k+

Next.js + Payload CMSの統合例

// Next.js App Router + Payload CMS(同一Next.jsプロジェクト内)
// app/(frontend)/articles/page.tsx
import { getPayloadHMR } from "@payloadcms/next/utilities";
import configPromise from "@payload-config";

export default async function ArticlesPage() {
  const payload = await getPayloadHMR({ config: configPromise });

  const articles = await payload.find({
    collection: "articles",
    where: { status: { equals: "published" } },
    sort: "-publishedAt",
    depth: 2,
  });

  return (
    <ul>
      {articles.docs.map((article) => (
        <li key={article.id}>
          <h2>{article.title}</h2>
          <p>{article.publishedAt}</p>
        </li>
      ))}
    </ul>
  );
}

コンテンツ管理・ナレッジツールはknowledgeカテゴリ(/categories/knowledge)で一覧でき、ローコード・ノーコードツールはローコードカテゴリ(/categories/low-code)でも探せます。

FAQ

Q. WordPressからヘッドレスCMSに移行すべきですか?

A. WordPressで不満がなければ移行する必要はありません。ヘッドレスCMSへの移行が有効なケース: フロントエンドをReact/Vueで構築したい・1つのAPIからWebサイトとモバイルアプリに同じコンテンツを配信したい・WordPressのセキュリティ問題(xmlrpc.php・プラグインの脆弱性)を避けたい・パフォーマンス最適化(Static Site Generation)が必要な場合です。

Q. StrapiはContentfulの代替になりますか?

A. 機能的にかなり近いです。Contentfulに比べてStrapiはセルフホストでデータを管理できる点・自由なカスタマイズ・費用を大幅に抑えられる点が優れています。一方でContentfulのグローバルCDN・エンタープライズSLAが必要な場合はContentfulが適しています。

Q. Directusは「既存のDBをAPIとして公開する」とはどういう意味ですか?

A. 既存のPostgreSQL・MySQLデータベースにDirectusを接続すると、テーブルをGUI上でコンテンツとして管理・REST/GraphQL APIを通じてデータにアクセスできます。既存のアプリケーションのDBを「ヘッドレスCMS化」して管理画面を追加するユースケースに最適です。StrapiやPayloadは新規プロジェクト向けですが、Directusは既存DBへの後付けに強みがあります。

Q. Payload CMSはNext.jsと同一プロセスで動かせますか?

A. Payload CMS v3以降はNext.jsのappディレクトリ内で動作する設計になっており、同一のNext.jsプロジェクトにCMS管理画面とフロントエンドを同居させられます。next dev1コマンドでCMSとフロントエンドが同時に立ち上がり、payload.find()でDB直接アクセス(API往復なし)でコンテンツを取得できます。Vercelへのデプロイもシンプルです。

まとめ

ユースケース推奨ツール
GUIで誰でも使えるCMSStrapi
既存DBをCMS化Directus
Next.jsと深く統合したコードファーストCMSPayload CMS
ContentfulからのOSS移行Strapi

関連外部リソース

他の記事も読む

Let's Build Together

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

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