Cloudflare Pagesで配信している静的サイトにいいねボタンを設置した
Cloudflareが分からん過ぎたので、N番煎じですがAstroで作った静的サイトにCloudflare Pages Functionsを使っていいねボタンを作ってみました。
全体的に雰囲気で書いてます。
前提
私のブログはWranglerを使ってCloudflare Pagesにデプロイしています。なので、Functionsを実装するだけで大体済みました。
Functionsにハローワールドする
記事ごとのいいね数をKVに記録するAPIを実装するために、Functionsを使います。
プロジェクトルートに/functions
ディレクトリを作って、ポコポコTSファイルを書いていけばFunctionが作れます。
概要についてはGet startedを、TypeScriptプロジェクトでの始め方はこのあたりを参考に進めました。
セットアップの中で@cloudflare/workers-types
を使ったブログ記事が多い印象でしたが、wrangler types
コマンドで型定義ファイルを作るのを推奨していそうな感じでした。
上記を参考に色々進めて、いったん次のようなディレクトリ構成です。いらなさそうなのは端折ってます。
.
├── functions
│ ├── hello-world.ts
│ ├── tsconfig.json
│ └── types.d.ts
├── public
│ └── <Astroのアセット>
├── src
│ └── <Astroのプロジェクトソースコード>
├── astro.config.mjs
├── package-lock.json
├── package.json
├── tsconfig.json
└── wrangler.toml
hello-world.ts
は/hello-world
にルーティングされます。とりあえず適当にレスポンス返すように実装しました。
export function onRequest(_context: any) {
return new Response("Hello, world!")
}
ローカルで動作確認したいので、wrangler pages dev
します。Functionsも立ち上がるはずです。
私の場合、次のような感じでnpm scriptsを用意して、npm run wrangler:dev
しました。
{
...
"scripts": {
"build": "npm run wrangler:types && astro check && astro build"
"wrangler:types": "wrangler types --path ./functions/types.d.ts",
"wrangler:dev": "npm run build && wrangler pages dev --local --live-reload ./dist",
...
},
...
}
ちなみにこの書き方だと、Astro側のコードを変更したらサーバーを立て直さないといけないです。面倒ですが。(ワークアラウンドあるっぽいですが、そんなに困らなかったので)
AstroのCloudflareアダプタのドキュメントにも似たようなことが書いてあります。
ナニハトモアレFunctionsは使えるようになりました。
記事にULIDを採番する
記事といいね数を紐づけるために、各記事のフロントマターにULIDを採番しておきました。
パス周りの情報を使ってもいいので必須じゃないですが、あとあと記事のパスとか変わったときに困りそうなので振っときました。
KVを作成する
npx wrangler kv namespace create <好きに命名する>
でKVを作れます。実行すると「この設定をwrangler.toml
に書いてね」と言ってくるので、追記しておきます。
多分こんな感じです。ローカルとProductionで使いたいので、トップレベルとenv.production
に書きました。
[[kv_namespaces]]
binding = "BLOG_736B_MOE_UPVOTE_COUNTER"
id = "dummy"
[[env.production.kv_namespaces]]
binding = "BLOG_736B_MOE_UPVOTE_COUNTER"
id = "dummy"
KV namespace IDがめっちゃ機微情報っぽいですが、ドキュメントを読む感じは公開情報として扱っていいだろうと解釈しています。
namespacesはNon-inheritable keysにあたり、トップレベルに書いても各環境に継承されないっぽいので、各環境で個別設定が必要でした。
ローカル環境での実行において、Functions同様にKVも特別な設定はいらなさそうで、wrangler pages dev
すればよさそうです。
wrangler pages dev
すると、KVを模倣するためのモロモロが.wrangler/state/v3/kv
配下に出現します。
FunctionsにAPIを実装する
あらかた準備できたので、/functions
ディレクトリに実装します。次のような構成にしました。
./functions
+├── api
+│ ├── _middleware.ts
+│ └── upvote.ts
├── hello-world.ts
├── tsconfig.json
└── types.d.ts
upvote.ts
では、記事に設定されたULIDをキーに取得・インクリメントする処理がメインです。変そうな処理が書かれていますが、いったんスルーしてください。
ついでに、_middleware.ts
で/api
配下にCORSヘッダを設定しておきました。
ヨシ!
一応変なリクエストはバリデーションする
好き放題KVに書き込まれると精神衛生上イマイチなので、post_id
が既知のULIDではない場合は書き込ませないようにしたいです。
既知のpost_id
をFunctionsに配信する方法で良い方法が思いつかず、次のようないびつそうな形で実現しました。
- AstroのIntegration APIを使って、ビルド時にMDXのフロントマターからULIDを引っ張り出して
/dist/data/ulids.json
に書き出す - FunctionsでHTTP経由で参照して検証する(
fetch("/data/ulids.json")
)
export default defineConfig({
// ...
- integrations: [sitemap(), mdx(), pagefind()],
+ integrations: [sitemap(), mdx(), pagefind(), collectUlid()],
// ...
})
このあたりの記事を参考にしましたが、想定解では無い感がすごいです。いい方法があれば知りたいです。
Astroでボタンを実装する
ボタンのコンポーネントを作って適当な場所に設置します。
こだわりはないと言うかこだわる余地もなさそうなので、親切な同僚(ChatGPT)に実装してもらいました。
できた
できました。
おわり
おわりです。安定稼働を祈ります。
Discussion