mcpField
V
vercel-kv

vercel-kv

github.com/jackspaceデータ・データベース1637 回インストール12 週間前に更新
リポジトリ

vercel-kv について

vercel-kv は、アプリケーションで Redis 互換の Vercel KV ストレージを使うためのスキルです。キャッシュや一時データを扱う開発者向けに、基本操作、バッチ処理、TTL とキー命名の指針を説明します。

主な機能

  1. 01キーと値の読み書き
  2. 02アトミックなカウンターと一括操作
  3. 03パイプラインによる複数コマンド実行
  4. 04キャッシュ、レート制限、セッションのパターン
  5. 05TTL とキー命名の実践

活用シーン

  1. 01アプリケーションデータのキャッシュ
  2. 02リクエストのレート制限の追跡
  3. 03有効期限付きセッションの保存
  4. 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 データベースの URL
  • KV_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 ダッシュボード

  1. Vercel プロジェクトに移動します
  2. Storage → データベースを作成 → KV を選択します
  3. データベースに名前を付けます
  4. 環境変数をコピーします

オプション 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 モック

公式ドキュメント


パッケージのバージョン(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 が返された場合のエラーハンドリングを実装済み
  • ローカル環境と本番環境でテスト済み

質問や問題がありますか?

  1. 公式ドキュメントを確認してください:https://vercel.com/docs/storage/vercel-kv
  2. Redis コマンドを確認してください:https://redis.io/commands
  3. Vercel ダッシュボードで使用状況をモニタリングしてください
  4. 環境変数が正しく設定されていることを確認してください

コメント