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!
ハマっていたので、この記事に助けられました。