🌍

Cloudflare Workers + KV + honoで簡単なAPIサーバを作る

2022/05/16に公開

Cloudflare Workers + KV + honoで簡単なAPIサーバーを作ってみたくて、ebaというモックAPIを作成できるAPIサーバーを書いてみた。突然適当なメソッドと適当なURLで適当なレスポンスを返すAPIが欲しくなったときに使える。
https://github.com/YuheiNakasaka/cloudflare-workers-eba?a=1

使い方としては、1) 専用の名前空間を作って、2) 好きなエンドポイントを設定する、だけ。こんな感じ。

 # 名前空間の作成。パスワードも適当に設定しておく。
 $ curl -X POST 'https://eba.razokulover.workers.dev/' -H 'X-REQUEST-TYPE:register' -d '{"password": "xxxxxxxx"}'
 {"namespace": "your-unique-namespace", "password": "xxxxxxxx"}

 # エンドポイントの作成
 # GET /hoge で {"msg": "Hello"} をjsonレスポンスで返してくれるエンドポイントを作る。
 # ついでにmy-original-headerというカスタムヘッダーも返してくれるようにしてみる。
 $ curl -X POST 'https://eba.razokulover.workers.dev/' \
   -H 'X-NAMESPACE:your-unique-namespace' \
   -H 'X-PASSWORD:xxxxxxxx' \
   -H 'X-REQUEST-TYPE:create' \
   -d '{"url": "/https/zenn.dev/hoge", "method": "GET", "statusCode": 200, "responseHeader": {"content-type": "application/json", "my-original-header": "XXX"}, "responseBody": {"msg": "Hello"}}'
 {"message": "ok"}

これで名前空間とエンドポイントが作成された。あとは作成したエンドポイントを叩くだけ。X-NAMESPACEの指定だけ常に必要になるので注意。

 $ curl -X GET -H 'X-NAMESPACE:your-unique-namespace' -D - 'https://eba.razokulover.workers.dev/hoge'
 HTTP/2 200
 content-type: application/json
 my-original-header: XXX

 {"msg": "Hello"}

技術

今回利用した技術としては下記。

  • 開発コマンド: wrangler2
  • サーバ: Cloudflare Workers
  • KVS: KV
  • framework: hono
  • 言語: TypeScript
  • ビルド: esbuild
  • テスト: jest
  • テスト環境: miniflare

HTMLなページのあるwebアプリならPages + functions か Workers + Sites の構成から選ぶことになるが、今回はAPIサーバのみなのでWorkers単体での利用になっている。詳しくはPages + Funcitons なのか Workers + Sites なのかをみて。

あと以降は開発環境の設定・テスト環境の設定・デプロイ設定などのコーディング以外の部分の情報が少なめのところを簡単に書いておく。

開発環境設定

wrangler2のインストール

 $ npm install -g wrangler

1.0ではなくcloudflare/wrangler2が入っていることを確認する。

init

 $ wrangler init my-worker

KVの設定

KV · Cloudflare Workers docs

KVの作成と設定

 $ wrangler kv:namespace create "MY_KV"
 $ wrangler kv:namespace create --preview "MY_KV"
 // wrangler.toml
 name = "my-worker"
 main = "src/index.ts"
 compatibility_date = "2022-05-15"
 kv_namespaces = [
   { binding = "MY_KV", id = "yyyyyyyyyyyyyyyy", preview_id = "xxxxxxxxxxxxxxxxx" }
 ]

その他wrangler.tomlに指定できるオプションについてはConfiguration · Cloudflare Workers docsを読むと良い

テスト環境の整備

依存関係のインストール

 $ npm i -D miniflare
 $ npm i -D jest jest-environment-miniflare
 $ npm i -D esbuild esbuild-jest
 $ npm i -D @types/jest

[miniflare https://miniflare.dev/]はCloudflare Workersのローカルシミュレータ。wrangler2.0からはwrangler dev --localとやると内部的にはminiflareが使われるようになっているらしい。明示的にinstallしているのはjestのtestEnvironmentとしてminiflareを使用したいので。[jest-environment-miniflare https://github.com/cloudflare/miniflare/tree/master/packages/jest-environment-miniflare]も同じくtestEnvironment用。

[esbuild https://esbuild.github.io/]は高速なビルドツール。wrangler2.0から内部で使われるようになっている。[esbuild-jest https://github.com/aelbore/esbuild-jest]はjestのesbuild用トランスフォーマー。

jestの設定

 // jest.config.js
 module.exports = {
   testEnvironment: "miniflare",
   testMatch: [
     "**/test/**/*.+(ts|tsx|js)",
     "**/src/**/(*.)+(spec|test).+(ts|tsx|js)",
   ],
   transform: {
     "^.+\\.(ts|tsx)$": "esbuild-jest",
   },
 };

tsconfig.jsonにjestの@typesの追加

 // tsconfig.json
 {
 	"compilerOptions": {
 		...
 		"types": [
 			"@cloudflare/workers-types",
 			"@types/jest" // <- 追記
 		]
 		...
 	}
 }

package.jsonのscriptsにコマンド追加

 // package.json
 {
 	...
 	"scripts": {
 		"start": "wrangler dev",
 		"publish": "wrangler publish",
 		"build": "esbuild --bundle --outdir=dist ./src/index.ts", // <- 追加
 		"dev": "wrangler dev --local", // <- 追加
 		"test": "npx jest --verbose"  // <- 追加
 	},
 	...
 }

開発

ローカルで開発サーバを立ち上げる

$ npm run dev

サーバーを書く

サンプルがてらKVを使った簡単なサーバー。MY_KVというネームペースがwrangler.tomlでWorkerにバインドされているのでdeclareしてしまえばスクリプトからput/getができる。

 declare const MY_KV: KVNamespace;

 addEventListener('fetch', (event: FetchEvent): void => {
   event.respondWith(this.handleEvent(event))
 })

 async function handleRequest(request) {
   const key = `${request.method}`
   await MY_KV.put(key, `Hello, ${request.method}`)
   const value = await MY_KV.get(key)
   return new Response(`message: ${value}`, {
     headers: { 'content-type': 'text/plain' },
   });
 }

参考

実際の開発のコードはhonoのexmaplesを参考にした。
https://github.com/honojs/examples

自分のコードも汚いけど参考にはなるかも。
https://github.com/YuheiNakasaka/cloudflare-workers-eba?a=1

デプロイ

wrangler publishとコマンドを叩くだけでデプロイ自体は簡単にできるんだけどせっかくなのでGitHub Actionsの設定もやった。

GitHub Actionsの設定

下記環境変数をGitHubに設定する。

  • CF_API_TOKEN
    • API Tokensで発行。Edit Cloudflare Workersというテンプレからほぼデフォルトのまま作成。
  • CF_ACCOUNT_ID
    • Cloudflare WorkersのダッシュボードのTOPからコピー。

.github/workflows/deploy.ymlは下記の通り。

 name: Deploy

 on:
   push:
     branches:
       - main

 jobs:
   deploy:
     runs-on: ubuntu-latest
     name: Deploy
     steps:
       - uses: actions/checkout@v2
       - uses: actions/setup-node@v2
         with:
           node-version: "14"
           cache: "npm"
       - name: Install Dependencies
         run: npm ci
       - name: Build
         run: npm run build
       - name: Publish
         uses: cloudflare/wrangler-action@2.0.0
         with:
           apiToken: ${{ secrets.CF_API_TOKEN }}
         env:
           CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}

その他

environmentsの設定

Workersの各種環境をprod/stg/devのような感じで分けて設定することができる。今回はやらなかったけど、ちょっと真面目に使うつもりなら設定したほうが良さそう。
https://developers.cloudflare.com/workers/platform/environments/

KV以外のストレージ

Durable Objectsという最低$5の一貫性やトランザクションのサポートのあるグローバルなKVSがある。今回は使わなかったけどKVだとwriteの反映に最大60秒かかったりして使いづらいこともあるのでちょっと真面目に使うつもりなら導入を検討したほうが良さそう。
https://developers.cloudflare.com/workers/learning/using-durable-objects/

来月以降はD1のbetaも来そう。SQLiteが使えるとより用途の幅が広がりそう。
https://www.publickey1.jp/blog/22/cloudflarecdnsqliterdbcloudflare_d1cdn.html

wrangler1ではなくwrangler2を使う

wranglerはついこの前wrangler2がリリースされた。miniflareというローカルシミュレータが組み込まれていたり何かと使いやすくなっているので特にこだわりなければwrangler2を使うと良い。

honoの開発が活発すぎる問題

ほぼ毎日何かしら開発されていて、マイナーバージョンレベルだとアップデートがガンガン行われている。これ事態別に悪いことではないんだけどREADMEやexampleのアプリとの乖離がちょくちょく発生したりして最初混乱した。コードもそんなに大きいものではないので困ったらソースコードを読むほうが早い。

参照リポジトリが少ない

wrangler2系を使った参照実装になるようなWorkersのリポジトリがそんなに多くないのでここどう書いたら良いのだろう???というような時に我流になりがち。まぁまだこの前正式にリリースされたばかりだから仕方ないけど。公式DocsにもExampleが増えていくといいなと思った。

wrangler.tomlをリポジトリに含めていいのか

wrangler.tomlにはCloudflare Workersに関する設定を記述するのだが、account_idやkvのidなどを含めてそのままGitHubに公開しても良いのかがわからなかった。account_idに関しては含めても良いというissueコメントを発見したが、kv_namespaceのidなんかも公開してしまっていいのだろうか。他の人たちのwrangler.tomlをみてみるとそのままidを含めている人もいればwrangler.example.tomlのような形で公開していない人もいた。API Tokenさえ漏れなければ別に公開していても大丈夫なんだろうとは思うけどこの辺はドキュメントに書いて欲しいなと思った。自分は現状クレカも登録してないし課金されることもないと思うのでwrangler.tomlの情報はGitに含めて公開してある。そもそも趣味アプリなので困ったらすぐ消せば良いし。

リソース

Discussion