D1を使ったコメントシステム
以前D1を使ったシンプルなコメントシステムを作った。
このシステムでは、コメント投稿時に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の呼び出し回数を劇的に節約できる、静的・動的ハイブリッドなコメントシステムが完成した。
記事をシェア
シェアカードを生成するかリンクをコピーして、みんなと共有しましょう。
続きを読む
別のルートへ
ほかの記事から一貫した基準で選出
最終更新日: (0日前)
一部の内容は古くなっている可能性があります
コメント