866 文字
4 分
D1で静的・動的ハイブリッドなコメントシステムを作った

D1を使ったコメントシステム#

以前D1を使ったシンプルなコメントシステムを作った。

https://stargazr.net/posts/2025/0316-astro-d1-comment-system/

このシステムでは、コメント投稿時にPagesプロジェクトのデプロイフックを叩いてサーバーサイド(SSG)でコメント描画を行っていた。
今回AstroブログテーマをShironeに移行するにあたり、コメントシステムをクライアントサイド描画するように改良した。

新コメントシステムの設計#

  • コメントAPIはCloudflare Workersで構築する
  • 投稿されたコメントはD1に格納すると同時にslugごとのjsonファイルを生成し、通常のコメントfetch時はこのjsonファイルを静的配信する
  • クライアントサイドでは、投稿ページでjsonファイルをfetchしコメントを描画する

コメントAPIの構築#

APIのほうは以前作ったのと大きく変わらない。

投稿endpoint(/api/comments/post)#

新規コメントをD1に格納した後、新規コメントを反映したjsonを生成しjson格納用Githubレポジトリにcommit&pushする。
その後、レスポンスボディに新規コメントを反映したjsonを返す。

コメントfetch endpoint(/api/comments/get/<slug>.json)#

該当slugのコメント一覧JSONを返す。
ただしこのエンドポイントは通常の状態では呼ばれない。

Workersの構成#

コメントfetch endpointのWorkersの処理がなるべく呼ばれないよう工夫する。

具体的には、Workers Assets機能を利用してjsonファイルをエンドポイント(/api/comments/get/<slug>.json)と同じパスで静的配信する。
この構成により、<slug>.jsonファイルが存在する場合はそちらの静的ファイルが優先して配信され、Workersの呼び出し回数が消費されない。
.jsonファイルがない場合および強制フェッチ用エンドポイント(.jsonなしのパス)が呼び出された場合はD1から最新のコメントを取得して返す。

コメントjsonファイルは、Workersのレポジトリにコメント格納用レポジトリをgit submoduleとして組み込み、Workersのデプロイ時にビルドコマンドでgit submodule update --init --recursive --remoteで取り込んでからデプロイする。

クライアントサイド#

投稿ページのjavascriptによりjsonファイルをfetchしてコメント一覧を描画する。

コメント投稿時はレスポンスボディのjsonを反映してコメント一覧をリフレッシュする。

システム動作の流れ#

以下が新コメントシステムのシーケンスダイアグラム。

sequenceDiagram
    autonumber
    actor User as 閲覧者 / 投稿者
    participant Astro as Astro Blog(SSG)
    participant Worker as Cloudflare Worker
    participant D1 as D1 Database
    participant Repo as GitHub (comments repo)
    participant WorkerRepo as GitHub (Worker repo)
    participant CI as Cloudflare デプロイ環境

    %% --- コメント読み込み ---
    break 記事ページを開いた場合
    User->>Astro: 記事ページを開く
    Astro->>Worker: GET /api/comments/get/#lt;slug#gt;.json
    alt 静的JSONが存在する場合 (Workers Static Assets)
        Worker-->>Astro: 200 OK (静的JSONを配信)
    else 静的JSONが未反映 / 存在しない場合 (Fallback)
        Worker->>D1: コメント一覧を取得 (SELECT)
        D1-->>Worker: コメントデータ
        Worker-->>Astro: 200 OK (D1取得データをJSON返却)
    end
    Astro-->>User: コメントを表示
    end

    %% --- コメント投稿 ---
    break コメントを投稿した場合
    User->>Astro: コメントを入力して送信
    critical Turnstile
    Astro->>Worker: POST /api/comments/post
    option Turnstile failed
    Astro->>Astro: Reject
    end
    Worker->>D1: 新規コメントを挿入 (INSERT)
    Worker->>D1: 最新のコメントを取得 (SELECT)
    D1-->>Worker: 最新コメントデータ
    Worker->>Repo: 更新した #lt;slug.json#gt;をcommit & push (GitHub API)
    Worker-->>Astro: 201 Created (最新コメントデータJSON)
    Astro-->>User: 画面のコメント一覧を即時更新
    break デプロイフック    
    Repo->>CI: デプロイフックを発火
    activate CI
    CI->>WorkerRepo: ソースコード取得
    CI->>Repo: git submodule update --init --recursive --remote
    Repo-->>CI: 最新の#lt;slug.json#gt;群を取得
    CI->>Worker: 静的アセットをバンドルして再デプロイ
    deactivate CI
    end
    end

この図にはないけどもう1点工夫した点があり、何かというとコメント投稿後数分間の対応である。

一応、コメント投稿時にレスポンスボディのjsonでコメント一覧を書き換えるのだが、このままだとページをリロードしたりすると古いコメントしか表示されない。これは静的配信されている.jsonファイルがまだ更新されていないため。
新規コメントが反映された.jsonファイルがコメント格納用レポジトリにpushされた際、Github ActionsでWorkersのデプロイフックを叩くが、デプロイが終わって反映されるまでは1分程度かかる。

ではどうするかというと、コメント投稿後数分間だけは強制fetch用エンドポイントにjsonを取りに行くようにすればいい。
この動作をどうやって実現するかかなり悩んだが、最終的にクッキーとCloudflareのURL書き換えルールを使って実現することができた。

コメント投稿時、APIからのレスポンスヘッダーにSet-Cookieを追加し、有効期限数分間のcomment-staleクッキーをセットさせる。
CloudflareのURL書き換えルールで、comment-staleクッキーがある場合は(/api/comments/get/<slug>.json)へのfetchを(/api/comments/get/<slug>)に書き換える。

これで常に最新のコメント情報を配信しつつ、Workersの呼び出し回数を劇的に節約できる、静的・動的ハイブリッドなコメントシステムが完成した。

D1で静的・動的ハイブリッドなコメントシステムを作った
https://stargazr.net/posts/2026/1011-new-clientside-comment-system/
作者
FarbiE
公開日
2026-10-11
ライセンス
CC BY-NC-SA 4.0

記事をシェア

シェアカードを生成するかリンクをコピーして、みんなと共有しましょう。

続きを読む

関連記事

共通するタグとカテゴリから

別のルートへ

ほかの記事から一貫した基準で選出

コメント