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を使う