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

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.バインディング名 でアクセスできるようになります。