🥶
microCMSでコンテンツの更新時にキャッシュをrevalidateする方法
はじめに
こんにちは!
今回は、Next.js+MicroCMSで、記事更新の際にキャッシュの影響で更新されない問題を解消する方法を紹介します。
細かいコードはgithubで公開しておりますのでご確認ください。
背景
Next.js+MicroCMSでの開発中に、microCMS側で記事を更新してもなかなか反映されないという問題に直面しました。
よくよく調べてみたところ、Next.jsのキャッシュの影響で反映がうまくいっていないことがわかりました。
この記事では、Webhookを利用して、キャッシュの再検証を行う方法を紹介したいと思います。
手順
1. microCMSの設定画面からwebhookを作る
api設定からwebhookの画面にいき、「追加」をクリックします。
サービスの選択で、「カスタム通知」を選択します。
シークレットに任意のシークレット値を入力しましょう
僕は、pythonで生成しました
# シークレットを生成
import secrets
random_hex = secrets.token_hex(20)
print(random_hex)
2. webhookからキャッシュをrevalidate
今回は、記事の送信時に指定したAPIにPOSTリクエストを送り、キャッシュの再検証を行なっています。
このAPIはmicroCMSからのリクエストのみを受け取るべきなので、シークレットを設定し検証をしています。
実装方法は以下のようになりました
// src/app/api/route.ts
import { revalidateTag } from "next/cache";
import { NextResponse } from "next/server";
import * as crypto from "crypto";
type RequestBody = {
id: string | null | undefined;
api: string;
};
export async function POST(request: Request): Promise<Response> {
const bodyText = await request.text();
const { id, api: endpoint } = JSON.parse(bodyText) as RequestBody;
const bodyBuffer = Buffer.from(bodyText, "utf-8");
const secret = process.env.MICROCMS_WEBHOOK_SIGNATURE_SECRET;
const signature = request.headers.get("X-MICROCMS-Signature");
if (!bodyText || !secret || !signature) {
console.error("Missing required information.");
return NextResponse.json({ status: 400, message: "Bad Request" });
}
// 値を検証
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(bodyBuffer)
.digest("hex");
const isValid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
if (!isValid) {
console.error("Invalid signature.");
return NextResponse.json({ status: 400, message: "Invalid Signature" });
}
// キャッシュの再検証
revalidateTag("articles");
return NextResponse.json({ message: "Success" });
}
シークレット値は、X-MICROCMS-Signature
というカスタムヘッダーに付与されています。
そこから、取得した値を検証しています。
その後、revalidateTag
で指定したタグのキャッシュを再検証しています。
タグは、データ取得関数で設定しています。
// src/libs/microcms.ts
export const getList = async (queries?: MicroCMSQueries) => {
const listData = await client.getList<Blog>({
endpoint: "blogs",
queries,
customRequestInit: {
// タグを設定
next: { tags: ["articles"] },
},
});
return listData;
};
参考文献
Discussion