Directus
データベース・ファーストのヘッドレスCMS。既存のデータベースを即座にAPIとして公開可能。
ヘッドレスCMS
Directus
概要
Directusは、既存のSQLデータベースに接続して即座にREST APIとGraphQL APIを生成する、オープンソースのヘッドレスCMSです。技術者でなくても使いやすい管理画面(Data Studio)と、強力なAPIを組み合わせ、あらゆる種類のプロジェクトに対応できる柔軟性を提供します。データモデルの設計からコンテンツ管理、API配信まで、すべてを一つのプラットフォームで実現します。
詳細
Directusは「Database First」アプローチを採用し、新規または既存のSQLデータベースをそのまま活用します。PostgreSQL、MySQL、SQLite、OracleDB、CockroachDB、MariaDB、MS-SQLなど、主要なデータベースをサポートし、マイグレーション不要で導入できます。TypeScriptで構築され、Node.jsとVue.jsをベースにした完全にオープンソースのプラットフォームです。
主な特徴:
- インスタントAPI生成: 既存DBから90秒以内にREST/GraphQL APIを自動生成
- Data Studio: 非技術者でも使いやすいVue.js製の管理画面
- データモデリング: Many-to-Any (M2A)関係を含む高度なデータ構造
- 拡張性: Extensions SDKによる完全なカスタマイズ
- ビルトイン機能: 認証、キャッシュ、画像変換、集計、フィルタリング
- Flows: イベント駆動型のデータ処理とタスク自動化
- アクセス制御: 詳細なロールベースのパーミッション
- リアルタイムサポート: WebSocketによるリアルタイム更新
メリット・デメリット
メリット
- 既存のデータベースをそのまま活用可能
- データベース構造に縛られない柔軟性
- 技術者・非技術者両方に優しいUI
- 完全なオープンソース(GPL-3.0)
- 豊富なデータベースサポート
- 強力な拡張機能システム
- APIパフォーマンスの高さ
- セルフホスティングとクラウドの両方に対応
デメリット
- データベースの知識が必要
- NoSQLデータベースは非対応
- 初期設定の複雑さ
- コンテンツタイプの事前定義が必要
- CMSに特化した機能が少ない
- 学習曲線がやや急
参考ページ
書き方の例
1. Hello World(基本的なセットアップ)
# Directusのインストール(Docker使用)
docker run -p 8055:8055 \
-e ADMIN_EMAIL="[email protected]" \
-e ADMIN_PASSWORD="password" \
-e KEY="your-random-key" \
-e SECRET="your-random-secret" \
directus/directus
# NPMでのインストール
npm init directus-project my-project
cd my-project
npx directus bootstrap
npx directus start
// Directus SDKの初期化
import { createDirectus, rest, authentication } from '@directus/sdk';
// クライアントの作成
const client = createDirectus('http://localhost:8055')
.with(authentication())
.with(rest());
// ログイン
await client.login('[email protected]', 'password');
// データの取得
const articles = await client.request(
readItems('articles', {
fields: ['id', 'title', 'content', 'author.name'],
limit: 10
})
);
2. コンテンツ管理
// コレクションとフィールドの作成(管理API使用)
import { createCollection, createField } from '@directus/sdk';
// 記事コレクションの作成
await client.request(
createCollection({
collection: 'articles',
meta: {
collection: 'articles',
icon: 'article',
display_template: '{{title}}'
}
})
);
// フィールドの追加
await client.request(
createField('articles', {
field: 'title',
type: 'string',
meta: {
interface: 'input',
display: 'formatted-value',
required: true
}
})
);
// リレーションの作成
await client.request(
createField('articles', {
field: 'author',
type: 'uuid',
meta: {
interface: 'select-dropdown-m2o',
display: 'related-values',
display_options: {
template: '{{first_name}} {{last_name}}'
}
},
schema: {
foreign_key_table: 'directus_users',
foreign_key_column: 'id'
}
})
);
3. API操作
// CRUD操作
import {
readItems,
readItem,
createItem,
updateItem,
deleteItem
} from '@directus/sdk';
// 複数アイテムの取得(フィルタリング付き)
const publishedArticles = await client.request(
readItems('articles', {
filter: {
status: {
_eq: 'published'
},
date_published: {
_lte: '$NOW'
}
},
sort: ['-date_published'],
limit: 20
})
);
// 単一アイテムの取得
const article = await client.request(
readItem('articles', 'article-id')
);
// アイテムの作成
const newArticle = await client.request(
createItem('articles', {
title: 'New Article',
content: 'Article content...',
status: 'draft'
})
);
// アイテムの更新
await client.request(
updateItem('articles', 'article-id', {
status: 'published',
date_published: new Date()
})
);
// 集計とグループ化
const stats = await client.request(
readItems('articles', {
aggregate: {
count: ['id'],
avg: ['views']
},
groupBy: ['status']
})
);
4. 認証設定
// 認証の設定
const client = createDirectus('http://localhost:8055')
.with(authentication('cookie', { credentials: 'include' }))
.with(rest());
// ログイン
await client.login('[email protected]', 'password');
// トークンの取得
const token = await client.getToken();
// 静的トークンの使用
import { staticToken } from '@directus/sdk';
const publicClient = createDirectus('http://localhost:8055')
.with(staticToken('your-static-token'))
.with(rest());
// カスタムストレージの実装
class CustomStorage {
get() {
return JSON.parse(localStorage.getItem('directus-auth'));
}
set(data) {
localStorage.setItem('directus-auth', JSON.stringify(data));
}
}
const client = createDirectus('http://localhost:8055')
.with(authentication('json', { storage: new CustomStorage() }))
.with(rest());
// 2要素認証の有効化
await client.request(enableTwoFactor('secret', 'otp'));
5. プラグイン・拡張機能
// カスタムエンドポイントの作成
// extensions/endpoints/custom/index.js
export default {
id: 'custom',
handler: (router, { services, env }) => {
const { ItemsService } = services;
router.get('/stats', async (req, res) => {
const articlesService = new ItemsService('articles', {
schema: req.schema,
accountability: req.accountability
});
const count = await articlesService.readByQuery({
aggregate: {
count: ['id']
}
});
res.json({ total_articles: count });
});
}
};
// カスタムフックの作成
// extensions/hooks/audit/index.js
export default ({ filter, action }, { services, database }) => {
const { ItemsService } = services;
filter('items.create', async (input, { collection }) => {
input.created_by = input.accountability?.user;
return input;
});
action('items.create', async ({ payload, collection }) => {
const auditService = new ItemsService('audit_log', {
schema: await getSchema()
});
await auditService.createOne({
collection,
action: 'create',
user: payload.accountability?.user,
timestamp: new Date()
});
});
};
// カスタムインターフェースの作成
// extensions/interfaces/custom-input/index.js
import InterfaceComponent from './interface.vue';
export default {
id: 'custom-input',
name: 'Custom Input',
icon: 'box',
component: InterfaceComponent,
options: [
{
field: 'placeholder',
name: 'Placeholder',
type: 'string'
}
]
};
6. デプロイ・本番環境設定
// 環境変数の設定
// .env
KEY=your-random-key
SECRET=your-random-secret
DB_CLIENT=postgres
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=directus
DB_USER=postgres
DB_PASSWORD=password
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=password
PUBLIC_URL=https://api.example.com
CORS_ENABLED=true
CORS_ORIGIN=https://app.example.com
// PM2での起動設定
// ecosystem.config.js
module.exports = {
apps: [{
name: 'directus',
script: 'npx',
args: 'directus start',
env: {
NODE_ENV: 'production',
PORT: 8055
}
}]
};
// Dockerでの本番環境構築
// docker-compose.yml
version: '3'
services:
database:
image: postgres:15
environment:
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus
POSTGRES_DB: directus
volumes:
- ./data/database:/var/lib/postgresql/data
directus:
image: directus/directus:latest
ports:
- 8055:8055
volumes:
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
environment:
KEY: ${KEY}
SECRET: ${SECRET}
DB_CLIENT: postgres
DB_HOST: database
DB_PORT: 5432
DB_DATABASE: directus
DB_USER: directus
DB_PASSWORD: directus
ADMIN_EMAIL: ${ADMIN_EMAIL}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
depends_on:
- database
// Nginxリバースプロキシ設定
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://localhost:8055;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}