Directusで簡単にデータ管理しよう

  • Web
  • 技術
  • Headless CMSと言えば、クラウドならContentfulやmicroCMS、オンプレミスならGhostやStrapiなどを想像する人が多いでしょう。

    VCbornの公式サイトも以前はGhostを使っていたのですが、カスタマイズ性があまり高くないことや日本語入力が時折おかしくなるなどの問題があり、他のものを探すことにしました。

    目次
    1. 選定
      1. Strapi
      2. Tina
      3. Payload CMS
    2. Directus
      1. 良いところ
      2. 悪いところ
    3. 導入
      1. セットアップ
      2. コレクションの作成
      3. アイテムの作成
      4. 権限設定
    4. Next.jsで取得
    5. まとめ

    選定

    Check out this showcase of some of the best, open source headless CMSes. This is…
    jamstack.org

    Headless CMSの選定にはJamstackのページを参考にしました。

    Strapi

    Ghostを導入する前に一度試しました。悪くはないのですが、localとproductionで一々編集してアップしてをするのは面倒だったので除外しました。

    Tina

    基本ローカルでもできるそうですが、production環境ではTina Cloudを使用しないといけないそうです。

    Payload CMS

    UIが超絶シンプルだったので候補になりましたが、対応DBがmongoDBのみでした。

    Directus

    Directus is an open-source data platform that instantly gives any new or existin…
    directus.io

    APIベースのデータ管理プラットフォームです。

    クラウド版のDirectus Cloudもありますが、GPL-3.0で提供されているセルフホスト版もあります。

    良いところ

    • セルフホストできる
    • MySQL(MariaDB)で使える
    • UIがシンプルでカッコいい
    • リポジトリの更新頻度が高い
    • 詳細な権限設定が可能

    悪いところ

    • ドキュメントが分かりづらい
    • 日本語記事がほぼない
    • クラウド版の一部機能が使えない

    導入

    セットアップ

    LTSのNode.jsが入っている環境で行ってください。

    まずdirectusのプロジェクトを作成します。

    npm init directus-project example-project

    MySQL/MariaDBが入っている環境の場合はアドレスやポート番号、ユーザー名・パスワード、データベース名を聞いてくるので、それらを入力します。

    管理者ユーザーは適当なユーザーにしましょう。

    Create your first admin user:
    ? Email: ainznino@pm.me
    ? Password: ********

    一通り終わるとプロジェクトが生成されるので、npx directus startでサーバーを開始します。

    デフォルトのポートは8055です。

    コレクションの作成

    初期状態ではまだ何も作成されていません。

    Settings > Data Modelから追加を選択し、欲しい形のモデルを作成します。

    例えば、VCbornのニュースだとこのようになっています。

    各項目の値はこのようにしています。

    id記事のパーマリンク
    title記事のタイトル
    thumbnail記事のサムネイル
    date_created投稿日
    date_updated更新日
    user_created作成したユーザー
    draft下書きかどうか
    content内容

    各フィールドは様々な物から選ぶことができます。

    アイテムの作成

    コレクションを作成し終えたら、それを元にアイテムを作ります。

    権限設定

    このままでは認証していないクライアントで画像を表示することができないので、権限をいじる必要があります。

    Settings > Roles & Permissionsを開き、PublicのSystem CollectionsからDirectus Filesの読み取りのみ許可してやります。

    Next.jsで取得

    データの作成はできたので、今度は実際にデータを取得して表示してみましょう。

    公式のNext.js用Exampleがあるのでそれを参考にして作っていきます。

    Integration Examples with Directus. Contribute to directus/examples development …
    github.com

    Directus SDKを既存のプロジェクトに入れておきます。

    npm i @directus/sdk

    認証用にトークンも生成しておきます。

    まずは認証済みのDirectusクライアントを作成します。

    import { Directus } from "@directus/sdk"
    import getConfig from "next/config"
    
    const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();
    const { url } = publicRuntimeConfig;
    const { email, password, token } = serverRuntimeConfig;
    
    type News = {
      id: string
      title: string
      thumbnail: string
      date_created: Date
      date_updated: Date
      user_created: string
      draft: Boolean
      content: string
    }
    
    type Collections = {
      news: News
    };
    
    
    const directus = new Directus<Collections>(url);
    
    export async function getDirectusClient() {
      if (email && password) {
        await directus.auth.login({ email, password });
      } else if (token) {
        await directus.auth.static(token);
      }
    
      return directus;
    }

    next.config.jsに環境変数の設定を追加します。

    const moduleExports = {
      ...
      publicRuntimeConfig: {
        url: process.env.DIRECTUS_URL,
      },
      serverRuntimeConfig: {
        token: process.env.DIRECTUS_STATIC_TOKEN,
      },
    }
    
    module.exports = moduleExports

    .envにも環境変数を追加しておきます。

    DIRECTUS_URL=http://localhost:8055
    DIRECTUS_STATIC_TOKEN=<生成したトークン>

    最後にgetDirectusClientでクライアントを呼び出し、queryすれば完成!

    import { format } from 'date-fns'
    import Image from 'next/image'
    import Link from 'next/link'
    import { getDirectusClient } from '@/lib/directus'
    
    const Home = (news) => {
      return (
        <div>
          {news.news.map((item, index) => {
                return (
                    <article key={item.id}>
                      <Link
                        className='mx-auto flex flex-col gap-8 md:flex-row md:items-center md:mx-0 duration-200 hover:bg-gray-100'
                        href={`/news/${item.id}`}
                      >
                        <div>
                          <Image
                            alt={item.id}
                            src={`http://localhost:8055/assets/${item.thumbnail}`}
                            width={360}
                            height={180}
                          />
                        </div>
                        <div>
                          <time className='font-semibold text-xl'>
                            {format(new Date(item.date_created), 'yyyy.MM.dd')}
                          </time>
                          <h3 className='font-semibold text-3xl'>{item.title}</h3>
                        </div>
                      </Link>
                    </article>
                )
              })}
        </div>
      )
    }
    
    export const getServerSideProps: GetServerSideProps = async (context) => {
      const directus = await getDirectusClient()
      const res = await directus.items('news').readByQuery({
        sort: ['-date_created'],
        filter: {
          draft: false,
        },
      })
      const news = res.data
    
      return {
        props: { news },
      }
    }

    まとめ

    大分気に入ったのでしばらくDirectusを使っていきます。

    ただ公式のSDKのドキュメントが大雑把で少しわかりづらかったです。

    アバター

    著者:wamo

    記事一覧

    ウェブ開発とか翻訳とかやってる人。

    コメントを書く

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です

    日本語が含まれない投稿は無視されますのでご注意ください。