Counts the Clouds

GraphCMS Management SDKでスキーマの生成とコンテンツのインポート
2022.05.27

GraphCMS
GraphQL

revolt-P7giqV_xSQA-unsplash.jpg

背景

既存のCMSの移行を想定していくつかのHeadless CMSを検討していたが、無料で小規模のCMSを運用できてインポートに対応してそうなGraph CMSを試してみることにした。

他にもMicroCMS(無料だと制限がきつい)、prismic.io(インポートが有料プランから)、Shifter(WordPressはちょっと)、sanity.io(CMSがセルフホストっぽい?)、Headless WordPress(WordPressは)などを検討した。

WordPressは妥当な選択肢ではあるが、いまどきクラシックテーマで独特なWordPress関数群と首っ引きになるのは時間がもったいない気がするし、新しいブロックテーマもHTMLタグ(というか特殊なコメント)で論理構造を記述し、JSONでスタイルを記述するというのはおよそ尋常とは思えない。

というわけで、なんとかしてHeadless CMSで道筋をつけたい。

管理画面での設定

まずはGraphCMSのウェブサイトで初期設定を行う。

プロジェクトの作成

アカウントはGitHubアカウントで作成。「Create a new project」の画面で「Blank」テンプレートから開始する。

Create a new project

「Project name」を入力。リージョンは「Japan(Tokyo)」を選択。「Create project」をクリック。

Project name and select region

プランは「Community(Free forever)」を選択する。

Select a plan

「Invite your team members」はスキップ。

Invite your team members

管理画面に到達。左メニュー下の歯車アイコンから「Settings」に移動。

Project home

エンドポイントの確認

「Environments」に移動して「Master Environment」のURL(エンドポイント)を控えておく。

Environment

認証トークンの設定

「API Access」に移動して一番下の「Permanent Auth Tokens」で「Create Token」。ここに「Management API」というエンドポイントがあるが今回は使わない。これは何のためにあるのだろう。内部的に呼んでいるとか?

API Accress

「Create token」ダイアログで、名前は適当に「Developer」などとして「Create & configure permissons」。

Create token

Management APIの権限設定

自動的にトークンの詳細を設定する画面に移動した。順番が前後するが、まず一番下の「Management API」から「Yes, initialize defaults」としてみる。

Token detail

3つほど権限が追加されたが、これでは足りないので「Edit Permissions」で権限を追加する。

適当に使いそうな権限をチェック。スキーマを送信するときに「Read existing environment」が必要なのでこれは必ず入れておく。

Content APIの権限設定

「Management API」の権限が設定できたら、コンテンツを送信するために「Content API」の権限も設定する(API Access画面ではなく、トークンに対して設定する)。「Create permission」をクリック。

適当に使いそうな権限を追加。今回はRead、Create、Update、Publishの権限をチェック。

「Content API」の権限が設定できた。

認証トークンの確認

「API Access」の画面に戻って、Developerトークンの「Value」を控えておく。

これで管理画面での設定ができた。

Management SDKでの作業

ここからは@graphcms/managementを使ってローカル環境からスキーマ定義とコンテンツを追加していく。

スキーマの作成については@graphcms/managementのリポジトリのREADMEにQuickstartがあるので参考にする。

コンテンツのインポートについては公式のMigrating to GraphCMSを参考にする。

% mkdir graphcms-sdk-trial; cd $_
% npm init -y
% npm install -D @graphcms/management graphql-request dotenv
% touch {migration,import}.js
% touch .env

.envファイルに認証情報を追加。管理画面で控えておいた「Environments」の「Master Environment」のURLと「Manegement Auth Token」を追加。

GRAPHCMS_ENDPOINT="https://api-ap-northeast-1.graphcms.com/v2/.../master"
GRAPHCMS_TOKEN="eyJh..."

スキーマの作成

migration.jsを実行することでスキーマを送信できるようにする。

% node ./migration.js

内容はほぼQuickstartの通り。ブログを意識しているのでモデル名はPostとしている。

require("dotenv").config();
const { newMigration, FieldType, Renderer } = require("@graphcms/management");

const migration = newMigration({
  endpoint: process.env.GRAPHCMS_ENDPOINT,
  authToken: process.env.GRAPHCMS_TOKEN,
  name: "createPostSchema",
});

const post = migration.createModel({
  apiId: "Post",
  apiIdPlural: "Posts",
  displayName: "Post",
});

post.addSimpleField({
  apiId: "title",
  displayName: "Title",
  type: FieldType.String,
});
post.addSimpleField({
  apiId: "content",
  displayName: "Content",
  type: FieldType.String,
  formRenderer: Renderer.MultiLine,
});

const changes = migration.dryRun();
console.log(changes);

まず、ドライランしてみる。console.logで以下の通りの出力を得る。

[
  {
    "createModel": {
      "apiId": "Post",
      "apiIdPlural": "Posts",
      "displayName": "Post"
    }
  },
  {
    "createSimpleField": {
      "apiId": "title",
      "displayName": "Title",
      "type": "STRING",
      "modelApiId": "Post",
      "formRenderer": "GCMS_SINGLE_LINE"
    }
  },
  {
    "createSimpleField": {
      "apiId": "content",
      "displayName": "Content",
      "type": "STRING",
      "formRenderer": "GCMS_MULTI_LINE",
      "modelApiId": "Post"
    }
  }
]

問題なさそうなので、実行できるようにする。

- const changes = migration.dryRun();
- console.log(changes);

+ // run migration
+ const foreground = true;

+ (async function () {
+   const result = await migration.run(foreground);

+   if (result.errors) {
+     console.log(result.errors);
+     return;
+   }
+   console.log(result.name);
+ })();
% node ./migration.js
createPostSchema

で、スキーマが作成できた。

Post schema

コンテンツのインポート

公式の記事では CSVを使っているが、ここでは簡単のため最初からJSONを使う。あと、Redisが必要そうなbee-queueは使わない。

この記事ではなんの脈絡もなくライブラリ由来のインスタンスが出てくるので面食らうが、サンプルをいくつか眺めるとわかる。

% touch import.json
[
  {
    "title": "投稿です",
    "content": "<strong>投稿です</strong>\n<a>http://example.com</a>"
  },
  {
    "title": "投稿2",
    "content": "あああああ\nあああああ\nあああああ"
  },
  {
    "title": "投稿3",
    "content": "<div>あああああ</div>"
  }
]

このJSONファイルを読み込んでコンテンツを送信するようにimport.jsのコードを追加する。

require("dotenv").config();
const fs = require("fs");
const { GraphQLClient, gql } = require("graphql-request");

const createContentEntry = async (row) => {
  const client = new GraphQLClient(process.env.GRAPHCMS_ENDPOINT, {
    headers: {
      Authorization: `Bearer ${process.env.GRAPHCMS_TOKEN}`,
    },
  });

  const query = gql`
    mutation createPost($title: String, $content: String) {
      createPost(data: { title: $title, content: $content }) {
        id
      }
    }
  `;

  return client.request(query, row);
};

const run = async () => {
  const data = JSON.parse(fs.readFileSync("./import.json", "utf8"));
  await Promise.all(
    data.map(async (row) => {
      return await createContentEntry(row);
    }),
  );
};

run();
% node ./import.js

実行すると、コンテンツをインポートできた。

Post content

コンテンツの公開

コンテンツが「Draft」になっているのでAPIを使って公開する。

% touch publish.js
require("dotenv").config();
const { GraphQLClient, gql } = require("graphql-request");

const publishContentEntries = async () => {
  const client = new GraphQLClient(process.env.GRAPHCMS_ENDPOINT, {
    headers: {
      Authorization: `Bearer ${process.env.GRAPHCMS_TOKEN}`,
    },
  });

  const query = gql`
    mutation {
      publishManyPostsConnection(first: 5, to: PUBLISHED) {
        edges {
          node {
            id
          }
        }
      }
    }
  `;

  return client.request(query);
};

const run = async () => {
  await publishContentEntries();
};

run();

GraphCMSではモデルを作成すると自動でmutationが生成される。createPostpublishManyPostsConnectionがそれにあたる。とても便利。

% node ./publish.js

公開できた。

Publish content

実際にCMSを移行するとなるとそれなりに苦労しそうではあるものの、コンテンツのインポートはできそうなことがわかった。

API Playground

左メニュー中ほどの右向き三角形アイコンから「Playground」に移動。実際にAPIのGraphQLのレスポンスを確認できる。

Playground