vercel-kv について
vercel-kv は、アプリケーションで Redis 互換の Vercel KV ストレージを使うためのスキルです。キャッシュや一時データを扱う開発者向けに、基本操作、バッチ処理、TTL とキー命名の指針を説明します。
主な機能
- 01キーと値の読み書き
- 02アトミックなカウンターと一括操作
- 03パイプラインによる複数コマンド実行
- 04キャッシュ、レート制限、セッションのパターン
- 05TTL とキー命名の実践
活用シーン
- 01アプリケーションデータのキャッシュ
- 02リクエストのレート制限の追跡
- 03有効期限付きセッションの保存
- 04カウンターのアトミックな更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill vercel-kvインストール後、Claude Code で/vercel-kvを入力して読み込まれたか確認します。
同梱ファイルREADME.mdSKILL.json
詳細
Vercel KV(Redis 互換ストレージ)
ステータス:本番環境対応
最終更新日:2025-10-29
依存関係:なし
最新バージョン:@vercel/[email protected]
クイックスタート(3 分)
1. Vercel KV データベースを作成する
# In your Vercel project dashboard
# Storage → Create Database → KV
# Pull environment variables locally
vercel env pull .env.local
以下が自動的に作成されます:
KV_REST_API_URL- KV データベースの URLKV_REST_API_TOKEN- 認証トークンKV_REST_API_READ_ONLY_TOKEN- 読み取り専用トークン(任意)
2. パッケージをインストールする
npm install @vercel/kv
3. アプリで使用する
Next.js サーバーアクション:
'use server';
import { kv } from '@vercel/kv';
export async function incrementViews(slug: string) {
const views = await kv.incr(`views:${slug}`);
return views;
}
エッジ API ルート:
import { kv } from '@vercel/kv';
export const runtime = 'edge';
export async function GET(request: Request) {
const value = await kv.get('mykey');
return Response.json({ value });
}
重要:
- 一時データには必ず TTL を設定します:
await kv.setex('key', 3600, value) - キーには名前空間を使用します:
${id}だけではなくuser:${id}:profileを使用します - JSON 値はシリアライズ可能である必要があります(関数や循環参照は不可)
5 ステップのセットアップ手順
ステップ 1:KV データベースを作成する
オプション A:Vercel ダッシュボード
- Vercel プロジェクトに移動します
- Storage → データベースを作成 → KV を選択します
- データベースに名前を付けます
- 環境変数をコピーします
オプション B:Vercel CLI
vercel env pull .env.local
以下が作成されます:
# .env.local (automatically created)
KV_REST_API_URL="https://xyz.kv.vercel-storage.com"
KV_REST_API_TOKEN="your-token-here"
KV_REST_API_READ_ONLY_TOKEN="your-readonly-token"
重要なポイント:
- プロジェクトごとに KV データベースを 1 つ使用することを推奨します
- 無料枠:月間 30,000 コマンド、ストレージ 256MB
- Vercel へのデプロイでは、環境変数が自動的に設定されます
ステップ 2:インストールと設定
npm install @vercel/kv
ローカル開発では、.env.local を作成します:
# .env.local
KV_REST_API_URL="https://your-db.kv.vercel-storage.com"
KV_REST_API_TOKEN="your-token"
本番環境では、環境変数が自動的に利用可能になります。
Cloudflare Workers(Vercel KV を使用):
# wrangler.toml
[vars]
KV_REST_API_URL = "https://your-db.kv.vercel-storage.com"
[[secrets]]
KV_REST_API_TOKEN = "your-token"
ステップ 3:基本操作
設定/取得:
import { kv } from '@vercel/kv';
// Set a value
await kv.set('user:123', { name: 'Alice', email: '[email protected]' });
// Get a value
const user = await kv.get('user:123');
// Returns: { name: 'Alice', email: '[email protected]' }
// Set with TTL (expires in 1 hour)
await kv.setex('session:abc', 3600, { userId: 123 });
// Check if key exists
const exists = await kv.exists('user:123'); // Returns 1 if exists, 0 if not
// Delete a key
await kv.del('user:123');
アトミック操作:
// Increment counter
const views = await kv.incr('views:post:123');
// Decrement counter
const stock = await kv.decr('inventory:item:456');
// Increment by amount
await kv.incrby('score:user:789', 10);
// Set if not exists (returns 1 if set, 0 if key already exists)
const wasSet = await kv.setnx('lock:process', 'running');
複数の操作:
// Get multiple keys
const values = await kv.mget('user:1', 'user:2', 'user:3');
// Returns: [{ name: '...' }, { name: '...' }, null]
// Set multiple keys
await kv.mset({
'user:1': { name: 'Alice' },
'user:2': { name: 'Bob' }
});
// Delete multiple keys
await kv.del('key1', 'key2', 'key3');
重要なポイント:
- 値は自動的に JSON としてシリアライズされます
- 存在しないキーには
nullが返されます - すべての操作はアトミックです
- TTL の単位は秒です
ステップ 4:高度なパターン
キャッシュパターン:
import { kv } from '@vercel/kv';
async function getPost(slug: string) {
// Try cache first
const cached = await kv.get(`post:${slug}`);
if (cached) return cached;
// Fetch from database
const post = await db.select().from(posts).where(eq(posts.slug, slug));
// Cache for 1 hour
await kv.setex(`post:${slug}`, 3600, post);
return post;
}
レート制限:
import { kv } from '@vercel/kv';
async function checkRateLimit(ip: string): Promise<boolean> {
const key = `ratelimit:${ip}`;
const limit = 10; // 10 requests
const window = 60; // per 60 seconds
const current = await kv.incr(key);
if (current === 1) {
// First request, set TTL
await kv.expire(key, window);
}
return current <= limit;
}
// Usage in API route
export async function POST(request: Request) {
const ip = request.headers.get('x-forwarded-for') || 'unknown';
if (!await checkRateLimit(ip)) {
return new Response('Rate limit exceeded', { status: 429 });
}
// Process request...
}
セッション管理:
import { kv } from '@vercel/kv';
import { cookies } from 'next/headers';
export async function createSession(userId: number) {
const sessionId = crypto.randomUUID();
const sessionData = { userId, createdAt: Date.now() };
// Store session for 7 days
await kv.setex(`session:${sessionId}`, 7 * 24 * 3600, sessionData);
// Set cookie
cookies().set('session', sessionId, {
httpOnly: true,
secure: true,
maxAge: 7 * 24 * 3600
});
return sessionId;
}
export async function getSession() {
const sessionId = cookies().get('session')?.value;
if (!sessionId) return null;
return await kv.get(`session:${sessionId}`);
}
パイプライン(一括操作):
import { kv } from '@vercel/kv';
// Execute multiple commands in a single round-trip
const pipeline = kv.pipeline();
pipeline.set('user:1', { name: 'Alice' });
pipeline.incr('counter');
pipeline.get('config');
const results = await pipeline.exec();
// Returns: ['OK', 1, { ... }]
ステップ 5:キーの命名規則
名前空間を使用する:
// ❌ Bad: No structure
await kv.set('123', data);
// ✅ Good: Clear namespace
await kv.set('user:123', data);
await kv.set('post:abc:views', 100);
await kv.set('cache:homepage:en', html);
命名パターン:
user:{id}:profile- ユーザープロフィールデータpost:{slug}:views- 投稿の閲覧数カウンターcache:{page}:{locale}- キャッシュされたページコンテンツsession:{token}- セッションデータratelimit:{ip}:{endpoint}- レート制限の追跡lock:{resource}- 分散ロック
重要なルール
必ず行うこと
✅ 一時データには TTL を設定する - メモリーリークや古いデータを防ぎます
✅ 名前空間付きのキーを使用する - 123 ではなく user:123 を使用します(衝突を防止)
✅ null の戻り値を処理する - 存在しないキーには null が返されます
✅ 複数の操作にはパイプラインを使用する - レイテンシーを削減します(ラウンドトリップが 1 回で済みます)
✅ JSON 互換のデータのみをシリアライズする - 関数や循環参照などは使用できません
✅ 分散ロックには SETNX を使用する - 競合状態を防ぎます
✅ コマンドの使用量を監視する - 無料枠の上限(月間 30K コマンド)以内に収めます
✅ 公開読み取りには読み取り専用トークンを使用する - セキュリティが向上します
絶対に行わないこと
❌ 暗号化せずに機密データを保存しない - KV はデフォルトでは保存時に暗号化されません
❌ TTL の設定を忘れない - TTL のないキーは無期限に残ります(メモリーリーク)
❌ 汎用的なキー名を使用しない - data、cache、temp は衝突します
❌ 大きな値(>1MB)を保存しない - 大きなファイルには Vercel Blob を使用します
❌ KV をプライマリデータベースとして使用しない - KV はキャッシュであり、永続ストレージではありません
❌ レート制限を超過しない - 無料枠では月間 30K コマンドです
❌ 高い耐久性を前提にしない - KV は一時データ用であり、重要なデータ用ではありません
❌ .env.local をコミットしない - KV トークンが含まれています(.gitignore に追加してください)
既知の問題の防止
このスキルは、10 件の既知の問題を防ぎます:
問題 #1:環境変数がない
エラー:Error: KV_REST_API_URL is not defined または KV_REST_API_TOKEN is not defined
参照元:https://vercel.com/docs/storage/vercel-kv/quickstart
発生理由:環境変数がローカルまたはデプロイ環境に設定されていません
防止策:vercel env pull .env.local を実行し、.env.local が .gitignore に含まれていることを確認します。
Issue #2:JSON シリアライズエラー
エラー:TypeError: Do not know how to serialize a BigInt または循環参照エラー
出典:https://github.com/vercel/storage/issues/89
発生する理由:JSON にシリアライズできないデータ(関数、BigInt、循環参照)を保存しようとしています。
予防策:プレーンなオブジェクト、配列、文字列、数値、ブール値、null のみを保存してください。BigInt は文字列に変換してください。
Issue #3:キー名の衝突
エラー:予期しないデータが返される、別の機能によってデータが上書きされる
出典:本番環境でのデバッグ、ベストプラクティス
発生する理由:異なる機能間で cache、data、temp のような汎用的なキー名を使用しています。
予防策:常に名前空間付きのキーを使用してください。feature:id:type のパターンが適しています。
Issue #4:TTL が設定されていない
エラー:メモリ使用量が無制限に増加する、古いデータが期限切れにならない
出典:Vercel KV のベストプラクティス
発生する理由:一時データに対して setex() ではなく set() を使用しています。
予防策:すべての一時データには setex(key, ttl, value) を使用してください。適切な TTL(秒)を設定してください。
Issue #5:レート制限超過(無料プラン)
エラー:Error: Rate limit exceeded またはコマンドの失敗
出典:https://vercel.com/docs/storage/vercel-kv/limits
発生する理由:無料プランの月間 30,000 コマンドの上限を超えています。
予防策:Vercel ダッシュボードで使用状況を監視し、必要に応じてプランをアップグレードしてください。キャッシュを使用して KV の呼び出しを減らしてください。
Issue #6:大きな値の保存
エラー:Error: Value too large またはパフォーマンスの低下
出典:https://vercel.com/docs/storage/vercel-kv/limits
発生する理由:KV に 1MB を超える値を保存しようとしています。
予防策:ファイルや画像には Vercel Blob を使用してください。KV の値は小さく保ってください(100KB 未満を推奨)。
Issue #7:Get 時の型の不一致
エラー:TypeScript のエラー、実行時の型エラー
出典:一般的な TypeScript の問題
発生する理由:kv.get() は unknown 型を返すため、型キャストまたは検証が必要です。
予防策:型アサーションと検証を使用してください。const user = await kv.get<User>('user:123') のように記述し、Zod で検証してください。
Issue #8:パイプラインのエラーが処理されない
エラー:サイレントな失敗、一部のみ実行される
出典:https://github.com/vercel/storage/issues/120
発生する理由:パイプラインの実行では、個々のコマンドが失敗する場合があります。
予防策:pipeline.exec() の結果配列を確認し、エラーを処理してください。
Issue #9:Scan 操作の非効率性
エラー:クエリが遅い、タイムアウトエラー
出典:Redis のベストプラクティス
発生する理由:大規模なデータセットに対して scan() を使用している、またはカーソルの処理が正しくありません。
予防策:count パラメーターを制限し、カーソルを適切に使って反復処理してください。本番環境では全件スキャンを避けてください。
Issue #10:TTL の更新漏れ
エラー:セッションの期限切れが早すぎる、キャッシュが予定より早く無効になる
出典:本番環境でのデバッグ
発生する理由:アクセス時に TTL を更新していません(スライディング有効期限)。
予防策:アクセス時に expire(key, newTTL) を使用して、スライディングウィンドウを実装してください。
設定ファイルのリファレンス
package.json
{
"dependencies": {
"@vercel/kv": "^3.0.0"
}
}
.env.local(ローカル開発)
# Created by: vercel env pull .env.local
KV_REST_API_URL="https://your-database.kv.vercel-storage.com"
KV_REST_API_TOKEN="your-token-here"
KV_REST_API_READ_ONLY_TOKEN="optional-readonly-token"
.gitignore
.env.local
.env*.local
一般的なパターン
パターン 1:キャッシュアサイド(遅延読み込み)
import { kv } from '@vercel/kv';
async function getUser(id: number) {
const cacheKey = `user:${id}`;
// Check cache
const cached = await kv.get<User>(cacheKey);
if (cached) return cached;
// Fetch from database
const user = await db.query.users.findFirst({
where: eq(users.id, id)
});
if (!user) return null;
// Cache for 5 minutes
await kv.setex(cacheKey, 300, user);
return user;
}
パターン 2:ライトスルーキャッシュ
import { kv } from '@vercel/kv';
async function updateUser(id: number, data: Partial<User>) {
// Update database
const updated = await db.update(users)
.set(data)
.where(eq(users.id, id))
.returning();
// Update cache
await kv.setex(`user:${id}`, 300, updated[0]);
return updated[0];
}
パターン 3:分散ロック
import { kv } from '@vercel/kv';
async function acquireLock(resource: string, timeout: number = 10) {
const lockKey = `lock:${resource}`;
const lockValue = crypto.randomUUID();
// Try to set lock (only if not exists)
const acquired = await kv.setnx(lockKey, lockValue);
if (acquired) {
// Set TTL to prevent deadlock
await kv.expire(lockKey, timeout);
return lockValue;
}
return null;
}
async function releaseLock(resource: string, lockValue: string) {
const lockKey = `lock:${resource}`;
const current = await kv.get(lockKey);
// Only delete if we own the lock
if (current === lockValue) {
await kv.del(lockKey);
}
}
// Usage
const lock = await acquireLock('process-orders');
if (lock) {
try {
await processOrders();
} finally {
await releaseLock('process-orders', lock);
}
}
パターン 4:リーダーボード
import { kv } from '@vercel/kv';
async function updateScore(userId: number, score: number) {
await kv.zadd('leaderboard', { score, member: userId.toString() });
}
async function getTopPlayers(limit: number = 10) {
// Get top scores (descending)
const top = await kv.zrange('leaderboard', 0, limit - 1, { rev: true, withScores: true });
return top;
}
async function getUserRank(userId: number) {
// Get user's rank (0-based)
const rank = await kv.zrevrank('leaderboard', userId.toString());
return rank !== null ? rank + 1 : null;
}
依存関係
必須:
@vercel/kv@^3.0.0- Vercel KV クライアントライブラリ
任意:
zod@^3.24.0- KV データの実行時型検証ioredis-mock@^8.9.0- テスト用の KV モック
公式ドキュメント
- Vercel KV:https://vercel.com/docs/storage/vercel-kv
- Vercel KV クイックスタート:https://vercel.com/docs/storage/vercel-kv/quickstart
- Vercel KV SDK リファレンス:https://vercel.com/docs/storage/vercel-kv/kv-reference
- GitHub:https://github.com/vercel/storage
- Redis コマンド:https://redis.io/commands(Vercel KV は Redis と互換性があります)
パッケージのバージョン(2025-10-29 に確認済み)
{
"dependencies": {
"@vercel/kv": "^3.0.0"
}
}
本番環境での活用例
このスキルは、Vercel KV の本番環境への導入事例に基づいています。
- Next.js の E コマース:セッション管理、カートのキャッシュ、レート制限
- ブログプラットフォーム:閲覧数カウンター、ページキャッシュ、API キャッシュ
- API ゲートウェイ:レート制限、レスポンスキャッシュ、分散ロック
- エラー:0 件(既知の問題 10 件をすべて防止)
- 稼働率:99.9% 以上(Upstash SLA)
トラブルシューティング
問題:KV_REST_API_URL is not defined
解決策:vercel env pull .env.local を実行して環境変数を取得してください。
問題:レート制限を超過した(無料プラン)
解決策:プランをアップグレードするか、クエリを最適化してください(複数の get 呼び出しの代わりに mget を使用し、キャッシュレイヤーを追加します)。
問題:値の有効期限が切れない
解決策:set() の代わりに setex() を使用するか、set() の後に expire(key, ttl) を呼び出してください。
問題:JSON のシリアライズエラー
解決策:値が JSON にシリアライズ可能であることを確認してください(関数、BigInt、循環参照は使用できません)。BigInt は文字列に変換してください。
セットアップの完全チェックリスト
- Vercel ダッシュボードで Vercel KV データベースを作成済み
- 環境変数をローカルに取得済み(
vercel env pull) -
@vercel/kvパッケージをインストール済み -
.env.localを.gitignoreに追加済み - キーの命名規則を確立済み(名前空間付きキー)
- 一時データすべてに TTL を設定済み
- レート制限のモニタリングを設定済み
- 型検証を実装済み(Zod スキーマ)
- null が返された場合のエラーハンドリングを実装済み
- ローカル環境と本番環境でテスト済み
質問や問題がありますか?
- 公式ドキュメントを確認してください:https://vercel.com/docs/storage/vercel-kv
- Redis コマンドを確認してください:https://redis.io/commands
- Vercel ダッシュボードで使用状況をモニタリングしてください
- 環境変数が正しく設定されていることを確認してください