api-database-vercel-kv について
サーバーレスおよびエッジアプリで Upstash Redis を使うためのパターンを扱うスキルです。REST ベースのクライアントを使った初期化、TTL、シリアライズ、バッチ処理、トランザクション、レート制限、セッション保存を説明します。
主な機能
- 01Redis.fromEnv() によるクライアント設定
- 02JSON シリアライズのパターン
- 03TTL と有効期限の設定
- 04ハッシュ操作
- 05パイプラインとアトミックなトランザクション
- 06レート制限とセッション保存
活用シーン
- 01API 応答やクエリ結果のキャッシュ
- 02エッジでのレート制限
- 03サーバーレスセッションの保存
- 04フラグや短期カウンターの管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-database-vercel-kvインストール後、Claude Code で/api-database-vercel-kvを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlreference.mdexamples/core.md詳細
Vercel KV / Upstash Redis のパターン
クイックガイド: サーバーレスおよびエッジ互換の Redis を REST API 経由で使用するには、
@vercel/kvの後継である@upstash/redisを使います。主な注意点:REST では TCP Redis と比べて呼び出しごとに約 5~15 ミリ秒のレイテンシが追加されます。すべての値は JSON として自動シリアライズされます(オブジェクトは透過的に往復しますが、Dateオブジェクトは文字列になります)。パイプラインとマルチは単一の HTTP リクエストとして実行されますが、パイプラインはアトミックではありません。接続を自動設定するにはRedis.fromEnv()を使います。サーバーレス Redis はコマンドごとに課金されるため、必ず TTL を設定してください。
<critical_requirements>
重要:このスキルを使用する前に
すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります(ケバブケース、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(新規プロジェクトでは必ず @upstash/redis を使用してください。@vercel/kv は 2024 年 12 月に非推奨となり、すべてのストアが Upstash Redis に移行されました)
(キャッシュするすべてのデータに必ず TTL を設定してください。サーバーレス Redis はコマンドごとに課金され、プランごとにストレージ上限があります)
(これは TCP Redis クライアントではなく、REST/HTTP クライアントであることを理解してください。コマンドごとに約 5~15 ミリ秒のオーバーヘッドを伴う HTTP リクエストが発生するため、可能な場合はパイプラインでまとめてください)
</critical_requirements>
例
- 基本パターン -- クライアントの設定、CRUD 操作、TTL、ハッシュ、パイプライン、トランザクション、レート制限、セッション
追加リソース:
- reference.md -- コマンドのクイックリファレンス、環境変数、プランの上限
自動検出: Vercel KV、@vercel/kv、@upstash/redis、Upstash Redis、KV_REST_API_URL、KV_REST_API_TOKEN、UPSTASH_REDIS_REST_URL、UPSTASH_REDIS_REST_TOKEN、Redis.fromEnv、kv.set、kv.get、kv.hset、kv.hget、kv.incr、kv.expire、kv.del、createClient、automaticDeserialization、エッジ Redis、サーバーレス Redis
使用する場面:
- Vercel のサーバーレス/エッジ関数で API レスポンスやデータベースクエリをキャッシュする
- エッジでのレート制限(スライディングウィンドウカウンター)
- サーバーレスアプリケーションのセッションストレージ
- フィーチャーフラグ、A/B テストの割り当て、一時的なカウンター
- TCP 接続を利用できない Vercel 上でのあらゆる Redis の用途(エッジランタイム)
対象となる主なパターン:
- クライアントの初期化(
Redis.fromEnv()、new Redis()) - JSON の自動シリアライズを利用した基本的な CRUD
- TTL と有効期限の戦略
- 構造化データ向けのハッシュ操作
- パイプライン(HTTP リクエストの一括処理)とトランザクション(アトミックな MULTI/EXEC)
- ソート済みセットを使ったレート制限
- セッションストレージのパターン
使用しない場面:
- 高スループット、低レイテンシの Redis ワークロード(リクエストごとにオーバーヘッドが追加される REST ではなく、TCP を使う ioredis を使用してください)
- Pub/Sub サブスクライバー(REST はリクエストとレスポンスの方式であり、永続的な接続ではありません)
- Redis Streams のコンシューマー(ioredis のような TCP クライアントが必要です)
- 大きな値の保存(無料プランではレコードあたり 1 MB 超。コマンド数に応じて課金されます)
- プライマリデータベース(Redis はキャッシュ/一時ストアであり、信頼できる唯一の情報源ではありません)
基本方針
Upstash Redis(旧 Vercel KV)は、TCP 接続が利用できない、または実用的でないエッジおよびサーバーレスのランタイム向けに設計された、サーバーレスの REST ベース Redis です。主なトレードオフは、リクエストごとのレイテンシオーバーヘッドを代償に、あらゆる環境で HTTP 互換性を得られることです。
基本原則:
- REST を優先 -- Redis の各コマンドは HTTP リクエストです。どこでも(エッジ、サーバーレス、ブラウザー)動作しますが、呼び出しごとに約 5~15 ミリ秒が追加されます。パイプラインでまとめてください。
- 自動シリアライズ -- オブジェクトは書き込み時に JSON としてシリアライズされ、読み取り時にデシリアライズされます。便利ですが、
Dateオブジェクト、Map、Set、関数は正確に保持されません。 - 一時利用を前提に設計 -- すべてに TTL を設定してください。サーバーレス Redis はコマンドごとに課金され、ストレージ上限があります。データベースではなく、キャッシュとして扱ってください。
- 接続管理が不要 -- コネクションプール、再接続ロジック、
errorイベントハンドラーは不要です。各リクエストはステートレスな HTTP です。
基本パターン
### パターン 1:クライアントの初期化適切な例と不適切な例を含む完全な実装:examples/core.md
方法は 2 つあります。Redis.fromEnv()(Vercel での使用を推奨します。UPSTASH_REDIS_REST_URL と UPSTASH_REDIS_REST_TOKEN を自動的に読み込みます)または、明示的に設定する場合は new Redis({ url, token }) です。認証情報をハードコードしないでください。
import { Redis } from "@upstash/redis";
const redis = Redis.fromEnv();
export { redis };
パターン 2:JSON の自動シリアライズ
SDK はオブジェクトを自動的に JSON にシリアライズして書き込み、読み込み時にデシリアライズします。手動で JSON.stringify を呼び出さないでください。二重シリアライズの原因になります。型付きの戻り値には get<T>() を、型安全な書き込みには satisfies を使用します。往復処理で Date オブジェクトは ISO 文字列になるため、タイムスタンプは数値として保存してください。
await redis.set("user:123", data satisfies UserProfile, { ex: TTL_SECONDS });
const user = await redis.get<UserProfile>("user:123"); // UserProfile | null
パターン 3:TTL と有効期限
TTL は必ず設定してください。サーバーレス Redis はコマンドごとに課金されます。set() には { ex: seconds } または { px: milliseconds } を使用します。分散ロックには { nx: true } を使用します("OK" または null が返されます)。TTL のないキーは、ストレージの上限のない増加を招きます。
await redis.set("cache:key", data, { ex: CACHE_TTL_SECONDS });
パターン 4:ハッシュ操作
ハッシュを使用すると、オブジェクト全体をシリアライズせずに、フィールドを部分的に読み書きできます。複数フィールドの書き込みには hset、読み込みには hget/hgetall、アトミックなカウンターには hincrby を使用します。注:hset は TTL を直接受け付けないため、別途 expire() を呼び出してください。hgetall はキーが存在しない場合、{} ではなく null を返します。
パターン 5:パイプラインとトランザクション
パイプライン(redis.pipeline())はコマンドを 1 つの HTTP リクエストにまとめますが、アトミックではありません。トランザクション(redis.multi())は、こちらも 1 つの HTTP リクエストでアトミックな MULTI/EXEC を実行します。複数のコマンドをまとめられる場合は、順次呼び出しを避けてください。呼び出しのたびに別の HTTP 往復が発生します。
const pipe = redis.pipeline();
pipe.set("k1", "v1", { ex: TTL });
pipe.incr("counter");
const results = await pipe.exec<[string, number]>();
重要: Upstash REST トランザクションは、楽観的ロックに使う WATCH をサポートしていません。
パターン 6:レート制限(スライディングウィンドウ)
ソート済みセットのスコアを使ったスライディングウィンドウです。タイムスタンプをスコアとして zadd で追加し、zremrangebyscore で期限切れのエントリを削除し、zcard で件数を数えます。これらはすべてパイプラインでまとめて実行します。本番環境でのレート制限には、組み込みアルゴリズムを提供する @upstash/ratelimit の使用を検討してください。
パターン 7:キャッシュアサイドヘルパー
汎用的な cacheAside<T>(key, fetcher, ttl) パターンです。まずキャッシュを確認し、キャッシュミス時にデータを取得します。キャッシュへの書き込みは、キャッシュの障害でレスポンスがブロックされないよう、完了を待たずに実行します。
<decision_framework>
選択の指針
Upstash Redis と ioredis/node-redis のどちらを選ぶべきですか?
Which Redis client should I use?
+-- Running in Vercel Edge Runtime? -> @upstash/redis (only option -- no TCP)
+-- Running in Vercel Serverless Functions? -> @upstash/redis (simpler) or ioredis (if you need TCP features)
+-- Need Pub/Sub subscribers? -> ioredis (REST cannot maintain subscriptions)
+-- Need Redis Streams consumers? -> ioredis (requires persistent TCP connection)
+-- Need lowest possible latency (<1ms)? -> ioredis with TCP (REST adds HTTP overhead)
+-- Simple caching/sessions/counters? -> @upstash/redis (zero connection management)
パイプライン、トランザクション、順次実行のどれを選ぶべきですか?
How should I batch commands?
+-- Need atomicity (all-or-nothing)? -> redis.multi() (transaction)
+-- Just reducing HTTP round-trips? -> redis.pipeline() (non-atomic batch)
+-- Single independent command? -> Direct call (redis.set, redis.get, etc.)
</decision_framework>
<red_flags>
注意すべき問題
優先度の高い問題:
- 新しいプロジェクトで
@vercel/kvを使用すること。2024年12月に非推奨となったため、代わりに@upstash/redisを使用してください - キャッシュしたキーにTTLを設定しないこと。ストレージが際限なく増加し、予期しない請求が発生します
- Upstash Redisで手動の
JSON.stringify/JSON.parseを使用すること。SDKがすべての値を自動でシリアライズするため、二重シリアライズが発生します - パイプラインコマンドがアトミックだと思い込むこと。パイプラインはHTTPの効率化のためにコマンドをまとめますが、アトミック性は保証しません(アトミックに実行するには
multi()を使用してください)
優先度の中程度の問題:
- パイプラインを使える場面でRedisへの呼び出しを逐次実行すること。呼び出しごとに個別のHTTP往復が発生します(1回あたり約5~15ms)
- 1 MBを超える値を保存すること。RESTリクエストにはプランごとのサイズ制限があります(無料プランおよび従量課金プランでは最大100 MBですが、大きな値はパフォーマンスを低下させます)
- Upstash Redisをプライマリデータベースとして使用すること。これはキャッシュ/一時データの保存先です。必ず別の場所に信頼できるデータソースを用意してください
よくある間違い:
- キーが存在しない場合に
hgetallが空のオブジェクト{}を返すと思い込むこと。Upstashはnullを返します({}を返すioredisとは異なります) - キーが存在しない場合、
get()はundefinedではなくnullを返すことを忘れること Dateオブジェクトが往復後もそのまま保持されると期待すること。ISO文字列にシリアライズされ、Dateインスタンスではなく文字列として返されます
注意点とエッジケース:
automaticDeserialization: falseを設定すると、多くのTypeScript型で問題が発生します。生の文字列レスポンスが必要で、型を手動で扱う準備ができている場合にのみ無効にしてくださいexオプションを指定したsetは、上書き時にTTLをリセットします(Redisの標準動作)。TTLがすでに設定されたキーに対してsetすると、新しいexの値で置き換えられます- RESTのレイテンシーはコマンドごとではなく、リクエストごとに発生します。10個のコマンドを含むパイプラインでも、単一コマンドと同じHTTPオーバーヘッド(1回の往復)です
- 無料プランは月間50万コマンド、ストレージ256 MBに制限されています。本番環境で使用量を監視してください
nx(キーが存在しない場合にのみ設定)は、失敗時にnull、成功時に"OK"を返します。戻り値を明示的に確認してください
</red_flags>
<critical_reminders>
重要な注意事項
すべてのコードはCLAUDE.mdのプロジェクト規約に従う必要があります(ケバブケース、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(新しいプロジェクトでは必ず@upstash/redisを使用してください。@vercel/kvは2024年12月に非推奨となり、すべてのストアはUpstash Redisに移行されました)
(キャッシュしたすべてのデータに必ずTTLを設定してください。サーバーレスRedisはコマンド単位で課金され、プランごとにストレージ制限があります)
(これはTCP Redisクライアントではなく、REST/HTTPクライアントであることを必ず理解してください。各コマンドは約5~15msのオーバーヘッドがあるHTTPリクエストとなるため、可能な限りパイプラインでまとめてください)
これらのルールに従わないと、非推奨パッケージの使用、際限のないストレージ費用、サーバーレス関数での不要なレイテンシーが発生します。
</critical_reminders>