Cloudflare KVをSvelteKitで使った話

Cloudflare KV(Key-Value Storage)はシンプルなキーと値のペアを保存するストレージです。D1のようなSQLクエリは不要で、get / put / delete だけで操作できます。


Cloudflare KV とは

KV D1
データ構造 キーと値のペア テーブル・リレーション
向いているデータ セッション・設定・キャッシュ ユーザー・投稿などの構造化データ
読み取り速度 非常に速い(エッジキャッシュ) 普通
クエリ なし(キー指定のみ) SQL
TTL(有効期限) ✅ 対応 ❌ 手動削除が必要

セットアップ

1. KV Namespaceを作成

npx wrangler kv namespace create VISIT_COUNTER

実行すると以下のような出力が出ます:

{ binding = "VISIT_COUNTER", id = "xxxxxxxxxxxxxxxx" }

2. wrangler.jsonc にバインディングを追加

"kv_namespaces": [
  {
    "binding": "VISIT_COUNTER",
    "id": "xxxxxxxxxxxxxxxx"
  }
]

3. src/app.d.ts に型を追加

interface Platform {
  env: {
    DB: D1Database;
    BUCKET: R2Bucket;
    VISIT_COUNTER: KVNamespace;  // ← 追加
    GITHUB_CLIENT_ID: string;
    GITHUB_CLIENT_SECRET: string;
  };
}

実装例:アクセスカウンター

src/routes/counter/+page.server.ts

import type { PageServerLoad, Actions } from './$types';

export const load: PageServerLoad = async ({ platform }) => {
  const kv = platform!.env.VISIT_COUNTER;
  const count = await kv.get('count');
  return { count: Number(count ?? 0) };
};

export const actions: Actions = {
  increment: async ({ platform }) => {
    const kv = platform!.env.VISIT_COUNTER;
    const current = await kv.get('count');
    await kv.put('count', String(Number(current ?? 0) + 1));
  },
  reset: async ({ platform }) => {
    const kv = platform!.env.VISIT_COUNTER;
    await kv.put('count', '0');
  },
};

src/routes/counter/+page.svelte

<script lang="ts">
  import { enhance } from '$app/forms';
  let { data } = $props();
</script>

<h1>アクセスカウンター</h1>
<p>カウント: {data.count}</p>

<form method="POST" action="?/increment" use:enhance>
  <button type="submit">+1</button>
</form>

<form method="POST" action="?/reset" use:enhance>
  <button type="submit">リセット</button>
</form>

KVの活用パターン

TTL付きの一時データ

有効期限を設定すると自動で削除されます。セッションやワンタイムトークンの管理に便利です。

// 10分後に自動削除
await kv.put('session:abc123', JSON.stringify(data), { expirationTtl: 60 * 10 });

JSONデータの保存と取得

第2引数で取得時の型変換を指定できます。

// 保存
await kv.put('config', JSON.stringify({ theme: 'dark', lang: 'ja' }));

// 取得('json'を指定するとJSONとしてパースされる)
const config = await kv.get('config', 'json');

取得時の型:'text''json''arrayBuffer''stream'

キーの一覧取得

プレフィックスで絞り込んで一覧取得できます。

const result = await kv.list({ prefix: 'session:' });
// result.keys → [{ name: 'session:abc' }, { name: 'session:xyz' }]

user:123:settings のような命名規則を使うと管理しやすくなります。

レート制限

TTLを使って一定時間内のリクエスト数を制限できます。

const key = `rate:${clientIp}`;
const count = Number(await kv.get(key) ?? 0);

if (count >= 10) {
  error(429, 'リクエストが多すぎます');
}

// 1分でリセット
await kv.put(key, String(count + 1), { expirationTtl: 60 });

フィーチャーフラグ

コードを変えずにCloudflareダッシュボードからON/OFFを切り替えられます。

const flag = await kv.get('feature:new-ui');
if (flag === 'true') {
  // 新UIを表示
}

D1との使い分け

ユースケース KV D1
セッション・トークン ✅ TTL付きで管理しやすい △ 手動削除が必要
ユーザー・投稿データ ❌ リレーションが作れない
サイト設定・フラグ ✅ シンプルに保存できる △ オーバースペック
レート制限 ✅ 高速・TTL対応 ❌ 遅い
全文検索 ✅ SQL LIKE が使える

まとめ

  • KVは get / put / delete だけで操作できるシンプルなストレージ
  • TTLを使った有効期限付きデータの管理が強み
  • セッション・レート制限・フィーチャーフラグなど、D1では大げさな用途に向いている
  • 構造化データやリレーションが必要な場合はD1を使う
← トップページに戻る