Cloudflare Workers KV の使い方と実例|Namespace からコードまで

※本記事にはプロモーション(広告・アフィリエイトリンク)を含みます。
Cloudflare Workers KV は、Workers から読み書きできるグローバルなキーバリューストアです。セッション管理・IP ブロックリスト・機能フラグなど、データベースを立てるほどではないが Workers 内だけには収められないデータの置き場として使われます。この記事では Namespace の作成から TypeScript での操作コード、TTL の実践的な使い方まで、公式ドキュメントをもとに再現できる手順で書きます。
Workers KV の特徴と料金
Workers KV は Cloudflare のエッジネットワーク(執筆時点で 200 以上のデータセンター)に分散されたキーバリューストアです。Workers の fetch ハンドラから非同期関数呼び出しで読み書きでき、外部 SDK や接続設定は不要です。
結果整合性という制約
KV は結果整合性を採用しています。書き込みは最終的にすべてのエッジへ伝播しますが、即時に全リージョンへ反映されるわけではありません。公式ドキュメントによると、書き込みが全エッジへ伝播するまで最大 60 秒かかる場合があります。
「定期的に更新される設定値をエッジで高速に読む」用途には向いており、「書いた直後に同じデータを強整合で読む」用途には向いていません。カウンタや在庫管理のような厳密な整合性が必要な場面では、後述する Durable Objects を検討してください。
料金(執筆時点)
| プラン | 読み取り | 書き込み | 削除 | ストレージ |
|---|---|---|---|---|
| Workers Free | 10万回/日 | 1,000回/日 | 1,000回/日 | 1 GB |
| Workers Paid | 1,000万回/月 | 100万回/月 | 100万回/月 | 1 GB 含む |
超過分の単価は公式サイトで確認してください(変動します)。
サイズ制限(公式ドキュメントより)
| 項目 | 上限 |
|---|---|
| キー | 512 バイト |
| バリュー | 25 MB |
| メタデータ | 1,024 バイト(JSON) |
| list 1回の最大取得件数 | 1,000 件 |
Namespace の作成とバインディング設定
1. Namespace を作成する
Wrangler CLI で作成します。Wrangler 3.x 以降が前提です。
wrangler kv namespace create "MY_KV"
成功すると以下のような出力が返ります。
Add the following to your configuration file in your kv_namespaces array:
{ binding = "MY_KV", id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
プレビュー(wrangler dev 用)の Namespace も別途作っておくと、本番データを汚さずに開発できます。
wrangler kv namespace create "MY_KV" --preview
2. wrangler.toml に記述する
[[kv_namespaces]]
binding = "MY_KV"
id = "本番用の Namespace ID"
preview_id = "プレビュー用の Namespace ID"
binding に書いた名前が、Workers コード内で env.MY_KV として参照するときのキーになります。preview_id を省略すると wrangler dev 実行時に警告が出ます。
TypeScript コードで KV を操作する
Env インターフェースの宣言
TypeScript を使う場合、Env インターフェースに KVNamespace 型を宣言します。
interface Env {
MY_KV: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// env.MY_KV で操作する
return new Response("ok");
},
};
put(書き込み)
// 文字列を保存
await env.MY_KV.put("user:123:session", "session-token-abc");
// JSON を文字列化して保存
await env.MY_KV.put(
"config:rate-limit",
JSON.stringify({ maxReq: 300, windowSec: 60 })
);
// TTL 付き(900秒 = 15分後に自動削除)
await env.MY_KV.put("ip:1.2.3.4:blocked", "1", { expirationTtl: 900 });
expirationTtl は秒単位で、公式ドキュメントによると最小値は 60 秒です。60 秒未満を指定するとエラーになります。
get(読み取り)
// 文字列として取得(存在しない場合は null)
const session = await env.MY_KV.get("user:123:session");
if (session === null) {
return new Response("Unauthorized", { status: 401 });
}
// JSON としてパースして取得
const configJson = await env.MY_KV.get("config:rate-limit", { type: "json" });
get は値が存在しない場合 null を返します(undefined ではありません)。!value で判定すると空文字列 "" も引っかかるため、value === null で明示的に判定してください。
delete と list
// 削除
await env.MY_KV.delete("user:123:session");
// プレフィックスで一覧取得
const result = await env.MY_KV.list({ prefix: "user:", limit: 100 });
for (const key of result.keys) {
console.log(key.name, key.expiration); // expiration は Unix タイムスタンプ
}
// ページネーション(list_complete が false なら続きがある)
if (!result.list_complete) {
const next = await env.MY_KV.list({ cursor: result.cursor });
}
実践例: TTL を活かしたレート制限とブロックリスト
TTL 付きの put は「一定時間が過ぎたら自動で消す」用途に適しています。以下はエッジで IP ごとのリクエスト数をカウントし、閾値を超えた IP を一定時間ブロックするシンプルな例です。
interface Env {
MY_KV: KVNamespace;
}
const RATE_LIMIT = 300; // 1分間の上限リクエスト数
const BAN_TTL = 900; // BAN の持続秒数(15分)
async function checkRateLimit(env: Env, ip: string): Promise<boolean> {
const banned = await env.MY_KV.get(`ban:${ip}`);
if (banned !== null) return false; // BAN 済み
const countKey = `count:${ip}`;
const raw = await env.MY_KV.get(countKey);
const count = raw ? parseInt(raw, 10) : 0;
if (count >= RATE_LIMIT) {
await env.MY_KV.put(`ban:${ip}`, "1", { expirationTtl: BAN_TTL });
return false;
}
// カウントアップ(60秒でウィンドウをリセット)
await env.MY_KV.put(countKey, String(count + 1), { expirationTtl: 60 });
return true;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const ip = request.headers.get("CF-Connecting-IP") ?? "unknown";
if (!(await checkRateLimit(env, ip))) {
return new Response("Too Many Requests", { status: 429 });
}
return new Response("OK");
},
};
2点注意があります。
- 結果整合性のため、バースト時に閾値を若干超えるリクエストが通る場合があります。 厳密な排他制御が必要なら Durable Objects を検討してください。
- 同一リクエスト内で KV を複数回呼ぶと、非同期レイテンシが積み上がります。
Promise.allでまとめて取得するか、取得済みの値を変数に保持して再利用してください。
CLI でのデータ操作とローカル開発
CLI でキーを直接操作する
# 書き込み
wrangler kv key put --binding MY_KV "test-key" "test-value"
# 読み取り
wrangler kv key get --binding MY_KV "test-key"
# 一覧
wrangler kv key list --binding MY_KV
# 削除
wrangler kv key delete --binding MY_KV "test-key"
本番 Namespace を直接操作するときは --binding の代わりに --namespace-id <id> を指定します。ただし本番データの意図しない書き換えに注意してください。
wrangler dev でのローカル確認
wrangler dev
wrangler dev はデフォルトでローカルにシミュレートされた KV を使います。本番 Namespace に接続したい場合は --remote フラグを追加しますが、誤操作リスクを避けるため基本はローカルで動作確認してからデプロイする順番をすすめます。
環境によって wrangler のバージョンが異なると、コマンド名や出力形式が変わる場合があります。wrangler --version で確認してから進めてください。
まとめ
Workers KV の操作は put / get / delete / list の4つに集約されます。TTL を指定すれば期限付きのデータ管理(セッション・ブロックリスト・一時フラグ)がコード数行で完結し、外部データベースを持ち込まずにエッジロジックを組めます。
一方、結果整合性という制約があるため、カウンタや排他制御が必要な場面では Durable Objects が適しています。用途に応じた使い分けを意識するのが前提です。
まず wrangler kv namespace create で Namespace を1つ作り、CLI から put と get を試してみてください。手が動いてから Workers コードに組み込む順番が、動作の感覚をつかむ近道です。
よくある質問
Cloudflare Workers KV は無料で使えますか?
Workers Free プランでは1日あたり読み取り10万回・書き込み1,000回・削除1,000回、ストレージ1GBまで無料で利用できます(執筆時点の公式料金より)。超過分は有料になるため、公式サイトで最新の単価を確認してください。
Workers KV と Durable Objects の違いは何ですか?
KV は結果整合性でグローバルに分散されるため高速読み取りに向きます。Durable Objects は強整合性を持ち、カウンタや予約管理など排他制御が必要な用途に向いています。「書いた直後に強整合で読む必要があるか」が使い分けの基準になります。
Workers KV の TTL に最小値はありますか?
公式ドキュメントによると、expirationTtl の最小値は 60 秒です。60 秒未満を指定するとエラーになります。短命なデータでも最低 60 秒の TTL が必要な点に注意してください。
wrangler kv namespace create を実行したら何が作られますか?
Cloudflare アカウント上に KV Namespace が作成され、一意の Namespace ID が発行されます。この ID を wrangler.toml の kv_namespaces に記述することで、Workers コードから env.バインディング名 でアクセスできるようになります。