オンデマンド ISRの機能を使って、CMSにあるコンテンツの変更をトリガーにページを更新してみた。
はじめに
Next.jsのv12.1でオンデマンドISRが追加されました。これは、revalidate関数を使用することで、ページごとに手動で再生成できるようになる機能です。
Next.jsの公式ブログにある通り、ISRのデメリットは、revalidateで設定した時間を経過しなければ再生成が行われない点でした。
ですが、Next.jsのv12.1で追加されたオンデマンドISRを使用すれば、任意のタイミングでページの再生成を行うことができるようになりました。
ということで今回は、microCMSというツールを使って、CMS内で更新されたコンテンツがISRによる再生成を待たずに、オンデマンドISRによって再生成できるようにできるか試してみます。
下記で作成したサイトに対して行う。
バージョンなどは上記スクラップにあります。
大まかな手順
です。なのでまずは
microCMS から Webhookを実行する設定
microCMSではコンテンツに関して何かしらしたらそれをトリガーにWebhookを実行することができます。
microCMSの「カスタム通知」という設定を使うことで、任意のURLに対してPOSTリクエストを実行できます。
今回はカスタム通知を以下のように設定しました。
第三者によるアクセスを防止、またはWebhook発行元がmicroCMSであることを検証可能にするため、シークレットという値を設定しています。
設定するとWebhookリクエストのリクエストヘッダにX-MICROCMS-Signature
が追加されます。この内容をもとにAPI側で認証を行います。
送受信されるデータは、microCMSがシークレット値とリクエストボディを組み合わせてSHA-256
を使用したハッシュベースのHMAC
(Hash Based Message Authentication Code
)で暗号化します。
シークレット値の生成などは公式通りに実施しました。
これで完了です。
ちなみにこのSwitch
一時的にWebhookの設定を有効/無効にしたい場合に用いるそうです。
Next.js で APIの作成
続いてAPIの実装をします。
公式のルール通り実装します。
pages/api/project.ts
に実装していきます。今回は対象が私の場合project
ページなだけで、ここは任意に書き換えてもいいです。
// Next.jsのAPIルートで使用されるリクエストとレスポンスの型をインポート
import type { NextApiRequest, NextApiResponse } from 'next'
const crypto = require('crypto')
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).send('Method not allowed')
if (!req.headers['x-microcms-signature']) return res.status(401).send('Invalid signature')
const signature = req.headers['x-microcms-signature'] as string
// シークレット値とリクエストボディをハッシュ化
const expectedSignature = crypto
.createHmac('sha256', process.env.MICROCMS_SIGNATURE)
.update(JSON.stringify(req.body))
.digest('hex')
if (signature !== expectedSignature) return res.status(401).send('Invalid signature')
try {
await res.revalidate('/projects')
return res.status(200).send('Revalidated')
} catch (err) {
return res.status(500).send('Error revalidating')
}
}
環境変数の設置もね
MICROCMS_SIGNATURE="micoCMSでシークレット値として設定した値です"
これまでのISRは revalidate
で指定した時間が経過されるまで反映されませんでした。
上記に書いたAPIルートで、revalidate('/projects')
を実行するとgetStaticProps
を使用しているprojectsページのキャッシュを再生成してくれます。
これで、ISRによるページ再生成を待たずに任意のタイミング再生成できます。
ちなみにgetStaticProps
内のrevalidate
も効くので指定時間後の再生成もできます。
シークレット値とリクエストボディをハッシュ化に関して
この部分に関して追記します。これはあとで署名(signature
)を検証するために変数に格納しています。
そのために、cryptoモジュールのcreateHmacメソッドを使用して、HMAC
オブジェクトを作成しています。
このメソッドで、指定したアルゴリズム(ここではSHA-256)とシークレット値(process.env.MICROCMS_SIGNATURE
)を使用して、HMACオブジェクトを作成しました。
続いて、HMACオブジェクト
のupdate
メソッドを使用して、リクエストボディのJSON文字列を更新しています。
続いて、digest
メソッドを使用して、HMAC値を16進数表記の文字列に変換しています。このメソッドは、HMAC
オブジェクトの内部状態から最終的なHMAC
値を生成し、その値を16進数表記の文字列に変換しています。
expectedSignature
変数に格納し、最終的に検証(microCMS経由かどうかを)するために使用します。
検証
自分は、以下の検証を通じて、オンデマンドISRが意図通り動いていることを確認しました。
ローカルにて
- ブラウザからアクセスをする(例えば今回なら
http://localhost:3000/api/projects
)
-Method not allowed
が返ることを確認(①) - ローカル環境に対してPostmanを使用してPOSTリクエストをする
- リクエストヘッダーに(以下②)- 何も指定しないでPOSTリクエストをする
-
Invalid signature
が返ることを確認
-
- keyは
x-microcms-signature
、valueは誤った値でPOSTリクエストをする-
Invalid signature
が返ることを確認
-
- keyは
x-microcms-signature
、valueは正しい値でPOSTリクエストをする-
Invalid signature
が返ることを確認- なぜか?
JSON.stringify(req.body)
←この中身が空なので
- なぜか?
-
- 何も指定しないでPOSTリクエストをする
本番環境にて
- 次は本番で試します
- その前に、vercelにも環境変数を設定することを忘れずに
- ISRによる更新と混同しないようにrevalidateの値を120(2分)とかに指定しておくといいでしょう
- ①と②の手順をして同じ結果になることを確認
- 最後に、microCMSからコンテンツの更新を加えて
- VercelのlogsのFunctionをAPIのパスのやつだけに絞って確認します
- 何かしら間違いがなければ
200
が返ってコンテンツが再生性(更新)されているでしょう
- 何かしら間違いがなければ
- VercelのlogsのFunctionをAPIのパスのやつだけに絞って確認します
- 最後に、microCMSからコンテンツの更新を加えて
以上です。
今から1年前の2022年2月に発表された機能で古いバージョンアップの検証ではありますが、自分のサイトでオンデマンドISRを使って、コンテンツの(ほぼ)即時更新ができるようにしました。
編集側が更新結果を(ほぼ)即時更新できる。かつサイトパフォーマンスもいい。良いとこどりですね。
(ほぼ)即時更新....感覚 5秒前後?
まだまだ初学者なので、誤りがあればご指摘ください!
参考
YouTube
Discussion