増分SSG — 変更検知APIで差分だけ再ビルド
投稿が数千件あるサイトを、更新のたびに全ページ再生成するのは時間がかかります。変更検知API(cms.changes)を使うと、前回ビルド以降に変更があった公開コンテンツ(新規・更新・公開・予約公開の到来・非公開・削除)だけを特定して、変更分だけ再生成できます。Webhook / GitHub Actionsが「ビルドを起動する」のに対し、変更検知APIは「どのページを再生成するかを決める」補完関係です。
@sharedan/cms-sdk を利用します。
1. 仕組み(差分と最終ビルド日時)
サイト単位の「最終ビルド日時」はCMS側が保持します。cms.changes.fetch() はその日時以降に更新された公開コンテンツの差分を返します。ポイントは3つです。
- 冪等:
fetch()は最終ビルド日時を進めません。ビルドが成功したときだけcomplete()で前進させます(失敗時は次回に同じ差分を再取得)。 - action:各変更は
upsert(現在公開中=再生成)かdelete(非公開・削除・未公開=出力から除去。元々無ければ無視)のどちらかです。 - full_resync:
trueのときはサイト全体を再生成します(changesに現在公開中の全コンテンツがupsertで並びます。詳細は §4)。
{
"as_of": "2026-10-14T12:34:56+09:00",
"full_resync": false,
"changes": [
{ "type": "post", "post_type": "news", "key": "summer-campaign",
"action": "upsert", "updated_at": "2026-10-14T10:00:00+09:00" },
{ "type": "post", "post_type": "news", "key": "old-notice",
"action": "delete", "updated_at": "2026-10-14T11:20:00+09:00" }
]
} 2. 差分を取得して変更分だけ生成
差分を取得し、upsert は該当ページを再生成、delete は出力から除去します。投稿詳細ページ(多数)だけを増分にし、トップや一覧ページは毎回再生成する構成が扱いやすいです(一覧はFAQ・固定コンテンツ・カテゴリの変更も反映するため)。
import { ShareDanCmsClient } from '@sharedan/cms-sdk';
const cms = new ShareDanCmsClient({
baseUrl: process.env.CMS_API_ORIGIN,
apiKey: process.env.CMS_API_KEY,
siteCode: process.env.CMS_SITE_CODE,
});
// 1. 前回ビルド以降の差分を取得する(最終ビルド日時はCMSが保持。fetch では進めない=冪等)
const feed = await cms.changes.fetch();
if (feed.full_resync) {
// 初回/カテゴリ・タグの変更/手動ビルド時は true。サイト全体を再生成する
await buildEverything();
} else {
for (const c of feed.changes) {
if (c.type === 'post' && c.action === 'upsert') {
// 現在公開中=再生成(post は c.post_type が種別slug、c.key が投稿slug)
const post = await cms.posts.get(c.post_type, c.key);
await writePage(post);
} else if (c.type === 'post' && c.action === 'delete') {
// 非公開・削除・未公開=出力から除去(元々無ければ無視)
await removePage('news', c.key);
}
// faq / content / category / form も type + action で同様に扱う
}
} delete の分は明示的に削除する必要があります(アップロードだけでは古いページが残るため)。
3. ビルド完了を通知する
ビルドとデプロイが成功したあとにだけ、取得した as_of を complete() に渡して最終ビルド日時を前進させます。これで次回の fetch() はこの時刻以降の差分だけを返します。
// 2. ビルド/デプロイが成功したら、取得した as_of で最終ビルド日時を前進させる。
// 失敗時は呼ばない=次回に同じ差分を再取得できる(取りこぼさない・冪等)。
await cms.changes.complete(feed.as_of); complete() を呼ぶと、その差分を「反映済み」とみなして次回から取りこぼします。成功時のみ呼び出してください。
4. 全体ビルドになる変更
次の場合は full_resync: true になり、サイト全体を再生成します。
- 初回(最終ビルド日時が未設定)。
- カテゴリ・タグ名の変更:カテゴリ名・タグ名は多数の投稿ページに波及するため、個別差分ではなく全体ビルドになります。
- サイト設定画面からの手動ビルド:管理画面で手動ビルドした場合も安全側で全体ビルドになります。
一方、フォーム定義の変更は局所的なため全体ビルドにはならず、type: "form" の差分として返ります。また、予約投稿が公開時刻に到来したとき(予約go-live)も、upsert の差分として現れます。
cms.changes を使ってどのページを再生成するかを決めます。
このページはお役に立ちましたか?
改善要望・不具合・ドキュメントの分かりにくい点など、お気軽にお寄せください。いただいた内容は今後の改善に役立てます。
お寄せいただきありがとうございます。今後の改善に役立てます。