2128 文字
11 分
Astroで作った静的ブログにCloudflare D1を利用してサーバレスでコメント機能を実装する!

astroで作った静的ブログにコメント機能をつけたい!!!!!!!#

当ブログはAstroで作成した静的サイトである。githubのリポジトリにあるmd資産を元に静的なhtmlを生成しているのでサイトの動きはサクサクでとてもよい。 が、静的サイトなので当然コメント機能などの動的機能は存在しない。実装するには外部のコメントサービスを利用するか、自分で実装するしかない。

「Astro コメント機能」などと検索すればいくつか参考になりそうなサービスは出てくるものの、githubのDiscussion機能を利用したGiscus(コメントにgithubログインが必要で一般的ではない)や広告の表示されるものなどがメイン。実装が簡単であるのは大きいが、コメントを残すためにgithubログインが必要だとただでさえ低いコメントされる可能性が更に低くなることが容易に想像できたのでGiscusは無し。広告も表示したくない。

また、コメントが投稿されるたびにデプロイを行いサイトをビルドし直してコメントを静的なサイトとして取り込む案も検討したが、どうせならリアルタイムでコメント更新されてほしいのでこの案も見送る。

ということでつい最近提供されはじめたらしいCloudflare D1をDBとして用いて自前のコメント機能を実装していくことに。 果たして実装完了まで漕ぎ着ける事はできるのか……。 実装完了しました。

構成#

資産:Githubリポジトリで管理

デプロイ:Cloudflare Pages

DB:Cloudflare D1

フレームワーク:Astro

Astroテンプレート:huwari

手順#

すでにAstroブログの初期設定は完了している前提です。

Cloudeflare Adapteのインストール#

AstroでSSRを利用するためには各デプロイ先サービスのアダプターが必要なため、今回はCloudeflare Adapteをインストール。

pnpm astro add cloudflare

yでインストール

pnpm astro add cloudflare
✔ Resolving packages...
Astro will run the following command:
If you skip this step, you can always run it yourself later
╭───────────────────────────────────────╮
│ pnpm add @astrojs/cloudflare@^12.6.0 │
╰───────────────────────────────────────╯
? Continue? » (Y/n)

インストール完了するとastro.config.mjsに下記のように追記される。

adapter: cloudflare(),

Wranglerのインストール#

Cloudflareを操作するために利用するCLIツールのWranglerのインストールを行う。Cloudflareの開発サーバの起動(Astro previewは使えなくなるので開発環境でビルドしてその内容をpreviewの代用として見る必要がある)やデータベースの作成、デプロイ等もranglerコマンドから可能(Github管理なので自動でデプロイされる為今回は使用しないが)。

pnpm install wrangler

インストールできたらバージョン確認コマンドを打ってみる。

pnpm wrangler --version
⛅️ wrangler 4.22.0
───────────────────

Drizzle ORMのインストール#

データベースを操作するためにDrizzle ORMに関係するパッケージのインストールを行う。

pnpm install drizzle-orm better-sqlite3

下記でインストールするdrizzle-kitは定義したスキーマファイルを利用してテーブルを作成/更新するために必要なマイグレーションファイルを作成するためのツール。

pnpm install -D drizzle-kit

Cloudflare D1のデータベース設定#

下記コマンドでデータベースを作成。

pnpm wrangler d1 create tarailife-post-comments
{
"d1_databases": [
{
"binding": "DB",
"database_name": "tarailife-post-comments",
"database_id": "2df89c19-da66-4200-96a5-2ed03756185e"
}
]
}

初実行時はブラウザが起動し認証画面が表示される。

認証画面が表示されている画像

認証を終えるとデータベースが作成されていることが確認できる。

テーブルが登録されていることが分かる画像

wrangler.tomlをプロジェクトルートに作成

[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "tarailife-post-comments"
database_id = "2df89c19-da66-4200-96a5-2ed03756185e"

Drizzleによる設定#

データベースのテーブルを作成する際に利用するsql文を作成するためにsrcフォルダの中にdbフォルダを作成しテーブルのスキーマ情報を記述したschema.tsファイルを作成。

今回は下記のような必要最低限のテーブルを取り敢えず作成。

import { text, integer, sqliteTable } from "drizzle-orm/sqlite-core";
export const comments = sqliteTable("comments", {
id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
author: text("author"),
content: text("content"),
post_slug: text("post_slug"),
post_date: text("post_date"),
});

プロジェクトルートにdrizzle.config.tsの作成

import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./drizzle",
driver: "d1-http",
dialect: "sqlite",
});

下記コマンドでマイグレーションファイルを生成。

pnpm drizzle-kit generate

コマンドを実行するとプロジェクトルートにdrizzleフォルダが作成される。

drizzleフォルダが作成されたことがわかる画像

テーブルの生成(.sqlファイルのファイル名は適宜上の手順で生成されたものに変更)。

pnpm wrangler d1 execute tarailife-post-comments --file=./drizzle/0000_useful_skreet.sql

.wrangler\state\v3\d1\miniflare-D1DatabaseObject\にファイルが3つ作成されているので確認。

drizzle.config.tsを更新(dbCredentialsurlを先程生成された.sqliteファイルのパスに変更)

import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./drizzle",
driver: "d1-http",
dialect: "sqlite",
dbCredentials: {
url: ".wrangler\state\v3\d1\miniflare-D1DatabaseObject\a520e4684331132e883183950c13a4f9c830d371f45a1f10dfdf654b438f8857.sqlite",
},
});

本番環境へテーブル作成(.sqlファイル名は生成されたものに変更)。

pnpm wrangler d1 execute tarailife-post-comments --remote --file=./drizzle/0000_useful_skreet.sql
⛅️ wrangler 4.22.0
───────────────────
? ⚠️ This process may take some time, during which your D1 database will be unavailable to serve queries.
Ok to proceed? » (Y/n)

DB の Binding 設定#

Pages にアップしたファイルからデータベースへのアクセスを行うためにD1 database bindingsの設定行う。Workers&Pagesのプロジェクト設定画面を開きバインディング追加を選択しD1データベースの情報を追加する。

バインディングが追加されている事がわかる画像

コメント表示用のコンポーネントを作成#

AstroではアイランドアーキテクチャでAstroファイル内にReactやSvelteなどの他フレームワークのコンポーネントを使用することができる他ページの一部を動的生成したり出来る。 今回はSSRを行うコメント表示用のコンポーネントをSvelteで作成し、それをページ生成用のAstroコンポーネントで読み込むことで各投稿ページにコメント機能を実装する。 表示している記事に投稿されているコメントだけ拾ってきたいのでリクエストパラメータでslugを渡しAPIを呼び出す。

src\components\widget\CommentBox.svelteを新規作成。

<script>
export let slug;
let author = "";
let content = "";
let comments = [];
const loadComments = async () => {
const res = await fetch(`/api/v1/comments/?slug=${slug}`, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
comments = await res.json();
};
const submitComment = async () => {
const res = await fetch("/api/v1/comments/", {
method: "POST",
body: JSON.stringify({ post_slug: slug, author: author, content: content }),
headers: { "Content-Type": "application/json" },
});
author = content = "";
await loadComments();
};
import { onMount } from "svelte";
onMount(loadComments);
</script>
<div class="grid grid-cols-1 gap-8">
<h3>コメント</h3>
{#each comments?.body?.comments as comment, i}
<div class="row-span-2 grid grid-rows-subgrid grid-rows-2 grid-cols-1 gap-2">
<div>
<strong>{`${i + 1}: ${comment.author || "no name"}`}</strong>
<small>{new Date(comment.post_date).toLocaleString()}</small>
</div>
<p>{comment.content}</p>
</div>
{/each}
</div>
<div class="grid grid-cols-1 gap-4 mt-8">
<h3>コメントを書く</h3>
<form on:submit|preventDefault={submitComment} class="row-span-4 grid grid-rows-subgrid grid-rows-2 grid-cols-1 gap-4 md:w-4/4">
<input bind:value={author} placeholder="名前" class="bg-[var(--license-block-bg)] rounded-md">
<textarea bind:value={content} placeholder="コメント内容" required class="h-24 bg-[var(--license-block-bg)] rounded-md field-sizing-content"></textarea>
<button type="submit" class="btn-regular h-8 text-sm px-3 rounded-lg">投稿</button>
</form>
</div>

作成したコンポーネントをコメント機能を追加したいページのコンポーネントに設置#

当ブログではAstroのブログテンプレートであるFuwariを使用しているためブログの各ポストを生成しているファイルはsrc\pages\posts\[...slug].astroになる。ここは各自でコメント機能を追加したいファイルに置き換えてコンポーネントを設置してほしい。

import CommentBox from "src/components/widget/CommentBox.svelte"; //先ほど作成したコメント表示コンポーネントをインポート
//コンポーネントを好きな箇所(といってもおそらくページ下部だと思うが)に設置
<div id="comments-container" class:list={["card-base z-10 px-6 md:px-9 pt-6 pb-4 relative w-full ",{}]}>
<CommentBox slug={entry.slug} client:load />
</div>

コンポーネントのpropsentry.slugで渡された値でどの記事に投稿されたコメントなのか判別している。

API用のページを作成#

今回はAstroのAPIエンドポイントとして、src\pages\api\v1\comments.tsのような配置でディレクトリを作成しAPIエンドポイント用のAstroファイルを作成している。

export const prerender = false;
import { drizzle } from "drizzle-orm/d1";
import { like } from "drizzle-orm"
import { comments } from "../../../db/schema";
import type { Runtime } from "@astrojs/cloudflare";
import type { D1Database } from "@cloudflare/workers-types";
export async function GET(context: { request: Request; locals: Runtime }) {
const { request, locals } = context;
const envDB = locals.runtime.env.DB as D1Database;
const db = drizzle(envDB);
const url = new URL(request.url);
const slug = url.searchParams.get("slug");
let comment = await db.select().from(comments).where(like(comments.post_slug, `${slug}`));
return new Response(
JSON.stringify({
body: {
comments: comment,
request: request,
},
}),
{ status: 200 },
);
}
export async function POST(context: { request: Request; locals: Runtime }) {
const { request, locals } = context;
if (!locals.runtime.env.DB) {
throw new Error("Database connection is missing");
}
const envDB = locals.runtime.env.DB as D1Database;
const db = drizzle(envDB);
const body = await request.json();
const comment = {
author: body.author,
content: body.content,
post_slug: body.post_slug,
post_date: new Date().toISOString(),
};
await db.insert(comments).values(comment);
return new Response(
JSON.stringify({
body: { request: request },
}),
{ status: 200 },
);
}

これで手順は終了。 下記は構築にあたって参考にさせていただいたサイトになります。

コメント欄実装完了!#

コメント欄実装できていることが分かる画像

これでシンプルだがコメント欄を実装できた。

コメントの返信機能だったりスパム対策のreCAPTCHA導入とか色々と直したいところもあるので今後も少しずつ改善していきたい。

Astroで作った静的ブログにCloudflare D1を利用してサーバレスでコメント機能を実装する!
https://tarailife.com/posts/add-astro-blog-comments-feature/
作者
keizokukun
公開日
2025-06-29
ライセンス
CC BY-NC-SA 4.0

コメント

コメントを書く