mcpField
A
api-database-vercel-kv

api-database-vercel-kv

github.com/agents-incデータ・データベース2657 回インストール2 日前に更新
リポジトリ

api-database-vercel-kv について

サーバーレスおよびエッジアプリで Upstash Redis を使うためのパターンを扱うスキルです。REST ベースのクライアントを使った初期化、TTL、シリアライズ、バッチ処理、トランザクション、レート制限、セッション保存を説明します。

主な機能

  1. 01Redis.fromEnv() によるクライアント設定
  2. 02JSON シリアライズのパターン
  3. 03TTL と有効期限の設定
  4. 04ハッシュ操作
  5. 05パイプラインとアトミックなトランザクション
  6. 06レート制限とセッション保存

活用シーン

  1. 01API 応答やクエリ結果のキャッシュ
  2. 02エッジでのレート制限
  3. 03サーバーレスセッションの保存
  4. 04フラグや短期カウンターの管理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agents-inc/skills --skill api-database-vercel-kv

インストール後、Claude Code で/api-database-vercel-kvを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.yaml
reference.md
examples/
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 互換性を得られることです。

基本原則:

  1. REST を優先 -- Redis の各コマンドは HTTP リクエストです。どこでも(エッジ、サーバーレス、ブラウザー)動作しますが、呼び出しごとに約 5~15 ミリ秒が追加されます。パイプラインでまとめてください。
  2. 自動シリアライズ -- オブジェクトは書き込み時に JSON としてシリアライズされ、読み取り時にデシリアライズされます。便利ですが、Date オブジェクト、Map、Set、関数は正確に保持されません。
  3. 一時利用を前提に設計 -- すべてに TTL を設定してください。サーバーレス Redis はコマンドごとに課金され、ストレージ上限があります。データベースではなく、キャッシュとして扱ってください。
  4. 接続管理が不要 -- コネクションプール、再接続ロジック、error イベントハンドラーは不要です。各リクエストはステートレスな HTTP です。

基本パターン

適切な例と不適切な例を含む完全な実装:examples/core.md

### パターン 1:クライアントの初期化

方法は 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>

コメント