Cloudflare Workers KV をローカルで使おうとしたらハマった
起こった問題と解決策
環境
MacOS: Ventura 13.4.1(c)
Wrangler: wrangler 3.5.0
言語: TypeScript
問題
- KV namespace 追加後にエラー
- 公式ドキュメント通りに動かそうとするとエラー
- ローカルで
get/putがエラー無く動いているはずなのにデータの参照/登録が行えない
原因と解決方法
- ローカルで KV namespace 追加時は
--preiewオプションが必要 - 直接記載はないが、Env に KV の定義が必要
- ローカルで実行した場合は
--previewの環境ではなくローカルのMiniflareに対して処理が実行される
以下は実際のコードを交えた解説です
KV の namespace 追加後にエラー
公式の手順に沿って wrangler を使って namespace を作成
$ wrangler kv:namespace create kv_test
🌀 Creating namespace with title "worker-test_kv_test"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "kv_test", id = "abcde12345" }
wrangler.toml に追加
name = "worker-test"``
main = "src/worker.ts"
compatibility_date = "2023-08-07"
kv_namespaces = [
{ binding = "kv_test", id = "abcde12345" }
]
wrangler dev でエラー
$ wrangler dev
✘ [ERROR] In development, you should use a separate kv namespace than the one you'd use in production.
Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and
add its id as preview_id to the kv_namespace "kv_test" in your wrangler.toml
※意訳
開発環境と本番環境で同じ kv namespace を使わないでね!
--previewオプションを付けて実行してpreview_idを発行してwrangler.tomlに設定してね!
--preview を使って開発環境用の namespace を作る
$ wrangler kv:namespace create kv_test --preview
🌀 Creating namespace with title "worker-test_kv_test"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "res_dev", preview_id = "fghij67890" }
wrangler.toml に追加
-{ binding = "kv_test", id = "abcde12345" }
+{ binding = "kv_test", id = "abcde12345", preview_id = "fghij67890" }
この状態で再度 wrangler dev を実行して、エラーが発生せず開発サーバが立ち上がりました
$ wrangler dev
[mf:inf] Ready on http://127.0.0.1:50119/
公式ドキュメント通りに動かそうとするとエラー
公式の手順に沿って workers 経由で KV から取得する処理を実装
wrangler init で生成された worker.ts を修正
export interface Env {}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext,
): Promise<Response> {
+ const id = env.kv_test.get("id");
+ return new Response(id);
- return new Response("Hello World!");
},
};
Property 'res_dev' does not exist on type 'Env'.ts(2339) のエラーが発生
Env に定義を追加してエラー解消
Env に kv_test の定義を追加
- export interface Env {}
+ export interface Env {
+ kv_test: KVNamespace;
+ }
エラーを解消することが出来た
ローカルで get/put がエラー無く動いているはずなのにデータの参照/登録が行えない
wrangler 経由で開発環境の kv namespace にデータを投入
$ wrangler kv:key put --binding=kv_test --preview "id" "100"
Writing the value "100" to key "id" on namespace fghij67890.
wrangler 経由でデータを確認
$ wrangler kv:key get --binding=kv_test --preview "id"
100
問題なく get/put が出来ている事を確認
workers 経由で KV からデータ取得
export interface Env {
kv_test: KVNamespace;
}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext,
): Promise<Response> {
const id = env.kv_test.get("id");
return new Response(id);
},
};
エンドポイントを叩いてみると返ってくる来るのは null でした
wrangler 経由で --preview で生成した namespace にデータがある事は確認できていたので、なぜ null になるのでしょうか?
実行時にエラーが発生している訳でもなさそうです
答え
前項で以下のように思い込んでいたのが原因でした
「ローカルでの実行時は
wrangler.tomlにセットしたpreview_idのnamespaceに接続できるようになるんだな」と思い込みます
KV namespace 作成時に --preview をつけるよう誘導されたので勘違いしていましたが、wrangler dev 実行時は preview_id の環境に接続しているわけではなさそうです
Starting with Wrangler v3, users running wrangler dev will be leveraging Miniflare v3 to run your Worker locally.
※意訳
Wrangler v3 からは
wrangler devは Miniflare v3 を通してローカルで worker を実行するよ!
ローカルで実行した場合の接続先は Cloudflare 上の Workers KV ではなく、ローカルで実行されている Miniflare に接続する仕様になっています
ローカルには project_dir/.wrangler/state/v3/kv というディレクトリが作成されていて、配下には KV namespace ごとにディレクトリがあります
ディレクトリの中を確認すると .sqlite ファイルがあるので、Miniflare を通して SQLite が動いているようです
ローカルでの実行確認
wrangler 経由で put してから get するのではなく、ローカル側で put してみます
export interface Env {
kv_test: KVNamespace;
}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext,
): Promise<Response> {
+ await env.kv_test.put("id", 111);
const id = env.kv_test.get("id");
return new Response(id);
},
};
この状態で API を叩くと 111 が返ってくるため、Miniflare を通して処理が行われていることが確認できました
Discussion
Awesome!
ハマっていたので、この記事に助けられました。