PowerCMS XのAPI利用に関する知見のまとめ

PowerCMS X Advent Calendar 2024 8日目です。このブログは2021年秋からPowerCMS XをヘッドレスCMSとして使用し、フロントエンドはNext.js v14で構築しています。ホスティングはVercelを利用していましたが、コストの都合で現在はAWS Amplifyを利用しています。2024年10月末からAmplify Gen2に変更し、より快適に閲覧できるようになりました。

さて、最近PowerCMS Xでも「ヘッドレス」や「API」について話題になったりお問い合わせを頂いたりします。そこで、PowerCMS Xに搭載されているAPIに関する知見を書いてみようと思います。公式ドキュメントは「RESTful API ドキュメント(概説) | PowerCMS X」からご覧ください。

API活用例

YouTubeショート動画のようにスワイプで次々と動画が閲覧できる画面をReact + TypeScriptで制作しました。動画を次々に表示させるためにAPIを利用してPowerCMS Xに蓄積した動画データを取得して画面に挿入しています。もしAPIがない場合、データを小分けにして取り出すことができないので大変そうです。
画面キャプチャ:iPhoneで作成したショート動画再生画面を表示している

その他、基幹システムとの連携(基幹システムの何らかのデータを読み込んでPowerCMS Xに登録する)でもAPIを活用することはできるのですが、今回はフロントエンドでの利用に的を絞ってい書いていきたいと思います。(PHPで開発するためPrototypeクラスを利用して操作したり、CSVをインポートしたりすることが多い現実があります。)

APIアクセスの基本

PowerCMS Xをインストールしているサーバーに/api/v1/スコープID/モデル名/メソッドのようなURLでアクセスします。例えばワークスペース1の記事一覧を取得する場合は/api/v1/1/entry/list、ワークスペース2にあるIDが1の記事を取得するには/api/v1/2/entry/get/1のようになります。公開されている情報を単に取得するだけ(認証が必要ないGETリクエスト)の場合は、ブラウザのアドレスバーにURLを直接入力することでレスポンスJSONを確認することができます。

プロジェクトにおいてはJavaScriptやPHPのコードからAPIにアクセスすることになりますが、JavaScriptの場合は「PTRESTfulAPIClient」、PHPの場合は「PowerCMS X RESTful API PHP Client」を利用すると、より簡単なコードで情報を取得できるようになります。例えば、Next.jsでPTRESTfulAPIClientを使用した場合、下記のようなコードで記事一覧が取得できます。

export const getStaticProps = async () => {
  const options: PCMSXRequestParams = {
    sort_by: 'published_on',
    sort_order: 'descend',
    limit: 5,
    cols: 'id,title,excerpt,card_image,published_on,basename',
  };

  const client = new PTRESTfulAPIClient('https://example.jp/powercmsx/api/index.php', 1); // PTRESTfulAPIClientを初期化
  const response = await client.listObjects('entry', 1, options); // モデル名・ワークスペースIDと取得条件を渡せば結果が返ってきます
  const entries: EntryList = await response.json();

  return {
    props: {
      entries: entries.items,
    },
  };
};

VSCode等をご利用頂いている場合は各メソッドの引数の説明などが表示されますので、ぜひご活用ください。
画面キャプチャ:VSCodeでPTRESTfulAPIClientクラスのメソッドにマウスオーバーすると引数の説明などが表示される様子

また、PTRESTfulAPIClientはAWS Lambdaでも利用できました。Lamdaを利用すれば、認証が必要なエンドポイントを利用する場合でも認証に必要なユーザー名・パスワードを隠しておくことができます。

JSONを静的生成して負荷を軽減

PowerCMS Xの場合、MTMLでビューを記述して静的JSONをパブリッシュこともできます。少しでも動的処理を減らしたい場合、記事を更新した時だけ内容が変わる単一記事オブジェクトのJSONは静的生成したJSONを利用するのも良いかもしれません。

外部システムとの連携

「外部システムとの連携に(PowerCMS Xの)APIを利用できますか?」という趣旨のお問い合わせをいただくことがありますが、PowerCMS XのAPIはPowerCMS Xに保存しているオブジェクトに対する読み書き操作を行うもので、外部システムの操作はできません。Slackに通知を送りたい、IFTTTを起動してSNSに投稿したいなど、外部システムを操作するには外部システムに対するWebhook(更新情報通知)を実装する必要があります。Webhookはプラグインで「post_saveコールバック」等を用いた実装により比較的容易に実現できると考えられます。

例えば、このサイトのようにヘッドレスCMSとして利用する場合、記事を更新した時等にWebhookでトリガーを引いてWebページをビルドする必要がありますが、独自プラグインで拡張し手動または自動でAWS AmplifyのWebhook URLに通知を出すようにしています。
PowerCMS X管理画面のDeploy Hookメニューで本番環境のトリガーボタンをクリックした後の画面

TypeScript型定義自動生成

JSONのTypeScript型定義を自動生成するツールも書いてみました。TypeScriptで開発する際に便利なのではないかと考えています。詳しくは当ブログの「RESTfulAPIで受け取るJSONのTypeScript型定義を自動生成してみる

APIレスポンスのカスタマイズ

プロジェクトにおける画面設計内容によっては規定の操作で情報が取得できない、取得しづらい、データを少し変更したい等があるかもしれません。このような場合は独自にプラグインを開発すると解決できる場合があります。高度な話になりますがこのようなこともできることを覚えておくといつかプロジェクトで役立つかもしれません。プラグインはPHPで開発を行います。

データの取得条件を操作したい

pre_listingコールバックを利用すると、データの取得条件を柔軟に操作することができます。冒頭の活用例でご紹介した動画プレーヤーでは動画のリストをAPIで取得する際、必ず指定したIDのオブジェクトが先頭に来る必要がありました。これを実現するにはSQLのORDER BY句にCASE式を追加する必要があるのですが、以下のようにpre_listingコールバックでCASE式を追加しました。

public function pre_listing_short_video(&$cb, $app, &$terms, &$args, &$extra) {
    if ($app->id === 'RESTfulAPI' && $app->param('app_name') === 'player') {
        $current_object_id = (int) $app->param('current_object_id');
        if (!$current_object_id) return;

        $extra .= " ORDER BY CASE WHEN `short_video_id` = {$current_object_id} THEN 1 ELSE 2 END,";
        $extra .= ' `short_video_published_on` DESC';
    }
}

レスポンスを返す直前でカスタマイズ

APIからは全てのカラムのデータがJSON形式で返却されます。パラメータcolsでカラム名を指定すると取得するカラムを絞り込むことができます。colsの指定ではできない以下のような場合、pre_responseコールバックでレスポンスを返す直前の$dataを操作することで希望のレスポンスJSONに仕上げることができます。

  • プラグインで実装したメソッドを実行したい(例えばHLS動画のURLをセットする)
  • JSONがシンプルになるようにデータの要素を加工したい
  • 追加の情報を入れたい
public function pre_response_restfulapi(&$cb, $app, &$data) {
    if ($app->model === 'short_video' && $app->method === 'list') {
        if ($app->param('app_name') !== 'player') return;

        $ctx = $app->ctx;

        for ($i = 0, $nData = count($data['items']); $i < $nData; $i += 1) {
            if (array_key_exists('upload_file_id', $data['items'][$i])) {
                // 独自メソッドの実行結果を返す
                if ($data['items'][$i]['upload_file_id']) {
                    $args['id'] = $data['items'][$i]['upload_file_id']['id'];
                    $m3u8_url = $this->hdlr_get_playlist_url($args, $ctx);
                    $data['items'][$i]['upload_file_id'] = (int) $args['id'];
                    $data['items'][$i]['m3u8_url'] = $m3u8_url ? $m3u8_url : null;
                } else {
                    $data['items'][$i]['m3u8_url'] = null;
                }
            }

            // asset_idカラムに格納しているサムネイル画像データのレスポンスを簡素にする
            if (array_key_exists('asset_id', $data['items'][$i])) {
                if ($data['items'][$i]['asset_id']) {
                    $data['items'][$i]['thumbnail_url'] = $data['items'][$i]['asset_id']['Permalink'];
                } else {
                    $data['items'][$i]['thumbnail_url'] = null;
                }
                unset($data['items'][$i]['asset_id']);
            }
        }

        // 独自データの追加
        $limit = $app->param('limit');
        $offset = $app->param('offset') ? (int) $app->param('offset') : 0;
        $data['hasMore'] = $offset + $limit < $data['totalResult'] ? true : false;
    }
}

カスタマイズにより得られるレスポンスは下記のようになります。

{
    "totalResult": 11,
    "hasMore": true,
    "items": [
        {
            "id": 1,
            "title": "アンブレラスカイ",
            "Permalink": "https:\/\/example.jp\/short_videos\/1.html",
            "m3u8_url": "https:\/\/example.jp\/short_videos\/hls\/00890001.m3u8",
            "thumbnail_url": "https:\/\/example.jp\/short_videos\/poster\/1.jpg"
        },

独自エンドポイントの追加

pre_listingpre_responseコールバックを利用したカスタマイズでも希望のレスポンスJSONが得られない場合、プロジェクト固有の要件がある場合などは、プラグインで独自のエンドポイントを追加して処理を行うことができます。希望の処理を書き、最後に$app->print_json($data);でデータを送信すると良いでしょう。

まとめ

PowerCMS XのAPIを利用すると蓄積しているデータがより活用できたり、高度なUIを作り込んだりすることができるようになります。APIを利用してより多くの人の生活を豊かにするアプリケーションをぜひ作ってみてください!