vercel-kv by jezweb/claude-skills
npx skills add https://github.com/jezweb/claude-skills --skill vercel-kv最后更新 : 2026-01-21 版本 : @vercel/kv@3.0.0 (Redis 兼容,由 Upstash 提供支持)
# 创建 KV: Vercel 仪表盘 → 存储 → KV
vercel env pull .env.local # 创建 KV_REST_API_URL 和 KV_REST_API_TOKEN
npm install @vercel/kv
基本用法 :
import { kv } from '@vercel/kv';
// 设置并指定 TTL(1 小时后过期)
await kv.setex('session:abc', 3600, { userId: 123 });
// 获取
const session = await kv.get('session:abc');
// 递增计数器(原子操作)
const views = await kv.incr('views:post:123');
关键提示 : 始终使用带命名空间的键(例如 user:123 而不是 123),并为临时数据设置 TTL。
缓存(旁路缓存):
const cached = await kv.get(`post:${slug}`);
if (cached) return cached;
const post = await db.query.posts.findFirst({ where: eq(posts.slug, slug) });
await kv.setex(`post:${slug}`, 3600, post); // 缓存 1 小时
return post;
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
速率限制 :
async function checkRateLimit(ip: string): Promise<boolean> {
const key = `ratelimit:${ip}`;
const current = await kv.incr(key);
if (current === 1) await kv.expire(key, 60); // 60 秒窗口
return current <= 10; // 每个窗口 10 次请求
}
会话管理 :
const sessionId = crypto.randomUUID();
await kv.setex(`session:${sessionId}`, 7 * 24 * 3600, { userId });
管道(批量操作):
const pipeline = kv.pipeline();
pipeline.set('user:1', data);
pipeline.incr('counter');
const results = await pipeline.exec(); // 单次往返
键命名 : 使用命名空间,例如 user:123、post:abc:views、ratelimit:ip:endpoint
始终 :
setex 而不是 set)user:123 而不是 123)绝不 :
此技能可预防 15 个已记录的问题 :
错误 : Error: KV_REST_API_URL is not defined 或 KV_REST_API_TOKEN is not defined 来源 : https://vercel.com/docs/storage/vercel-kv/quickstart | GitHub Issue #759 原因 : 本地或部署时未设置环境变量。在单体仓库(Turborepo/pnpm 工作区)中,将 @vercel/kv 抽象到共享包中可能导致 Vercel 构建失败,即使本地构建正常。预防 : 运行 vercel env pull .env.local 并确保 .env.local 在 .gitignore 中。对于单体仓库,可以 (1) 在消费应用而非共享包中创建客户端,(2) 使用 Vercel 环境变量 UI 在项目级别设置,或 (3) 将环境变量添加到 turbo.json 流水线配置中:{ "pipeline": { "build": { "env": ["KV_REST_API_URL", "KV_REST_API_TOKEN"] } } }。
错误 : TypeError: Do not know how to serialize a BigInt 或循环引用错误。此外,hset() 会将数字字符串强制转换为数字。来源 : https://github.com/vercel/storage/issues/89 | GitHub Issue #727 原因 : 尝试存储不可 JSON 序列化的数据(函数、BigInt、循环引用)。此外,当使用 hset() 存储看起来像数字的字符串值(例如 '123456')时,hgetall() 会将它们作为数字返回,破坏了类型一致性。预防 : 仅存储普通对象、数组、字符串、数字、布尔值、null。将 BigInt 转换为字符串。对于包含数字字符串的哈希字段,可以 (1) 使用非数字前缀,如 'code_123456',(2) 存储为 JSON 字符串并在检索后解析,或 (3) 在 hgetall() 之后验证并重新转换类型:String(value.field)。
错误 : 返回意外数据,数据被不同功能覆盖 来源 : 生产环境调试,最佳实践 原因 : 在不同功能中使用通用键名,如 cache、data、temp 预防 : 始终使用带命名空间的键:遵循 feature:id:type 模式。
错误 : 内存使用量无限增长,旧数据永不过期 来源 : Vercel KV 最佳实践 原因 : 对临时数据使用 set() 而不是 setex() 预防 : 对所有临时数据使用 setex(key, ttl, value)。设置适当的 TTL(秒)。
错误 : Error: Rate limit exceeded 或命令执行失败 来源 : https://vercel.com/docs/storage/vercel-kv/limits 原因 : 在免费套餐上超出每月 30,000 次命令限制 预防 : 在 Vercel 仪表盘中监控使用情况,必要时升级套餐,使用缓存来减少 KV 调用。
错误 : Error: Value too large 或性能下降 来源 : https://vercel.com/docs/storage/vercel-kv/limits 原因 : 尝试在 KV 中存储大于 1MB 的值 预防 : 对文件/图像使用 Vercel Blob。保持 KV 值较小(建议小于 100KB)。
错误 : TypeScript 错误,运行时类型错误。泛型 kv.get<T>() 有时即使数据存在也返回 null。来源 : 常见 TypeScript 问题 | GitHub Issue #510 原因 : kv.get() 返回 unknown 类型,需要进行类型转换或验证。此外,存在一个类型推断错误,使用泛型如 kv.get<T>() 可能导致函数返回 null,即使 CLI 显示数据存在,这是由于序列化/反序列化问题。预防 : 不要对 get() 使用泛型。相反,检索时不使用类型参数,在检索后进行类型转换:const rawData = await kv.get('key'); const data = rawData as MyType | null;。在使用前使用 Zod 或类型守卫进行验证。
错误 : 静默失败,部分执行 来源 : https://github.com/vercel/storage/issues/120 原因 : 管道执行中可能出现单个命令失败 预防 : 检查 pipeline.exec() 返回的结果数组并处理错误。
错误 : 查询缓慢,超时错误。在 v3.0.0+ 中,游标类型从 number 更改为 string。来源 : Redis 最佳实践 | Release Notes v3.0.0 原因 : 在大数据集上使用 scan() 或游标处理不当。版本 3.0.0 引入了一个破坏性变更,扫描游标现在是 string 而不是 number。预防 : 限制 count 参数,正确使用游标迭代,避免在生产环境中进行全量扫描。在 v3.0.0+ 中,使用 let cursor: string = "0" 并与 cursor !== "0" 比较(而不是 !== 0)。
错误 : 会话过早过期,缓存过早失效 来源 : 生产环境调试 原因 : 访问时未刷新 TTL(滑动过期) 预防 : 在访问时使用 expire(key, newTTL) 来实现滑动窗口。
错误 : 使用 kv.scanIterator() 时,for await 循环永不终止 来源 : GitHub Issue #706 原因 : v2.0.0+ 中存在一个错误,迭代器未正确发出完成信号。迭代器能正确处理键但永不退出,导致函数无法返回。同样影响 sscanIterator()。预防 : 使用带游标的手动 scan() 代替 scanIterator()。
// 不要使用 scanIterator() - 在 v2.0.0+ 中会挂起
for await (const key of kv.scanIterator()) {
// 此循环永不终止
}
// 改用带游标的手动 scan
let cursor: string = "0"; // v3.x 使用字符串
do {
const [newCursor, keys] = await kv.scan(cursor);
cursor = newCursor;
for (const key of keys) {
const value = await kv.get(key);
// 处理键/值
}
} while (cursor !== "0");
错误 : kv.zrange(key, 0, -1, { rev: true }) 即使数据存在也返回空数组 来源 : GitHub Issue #742 原因 : SDK 在处理某些键模式的反向标志时存在错误。CLI 始终返回正确的值。移除 rev 标志可以正确返回数据。预防 : 省略 rev 标志并在内存中反转,或使用 zrevrange() 代替。
// 这有时会返回空数组(BUG)
const chats = await kv.zrange(`user:chat:${userId}`, 0, -1, { rev: true });
// [] - 但 CLI 显示有 12 个项目
// 解决方法 1: 省略 rev 标志并在内存中反转
const chats = await kv.zrange(`user:chat:${userId}`, 0, -1);
const reversedChats = chats.reverse();
// 解决方法 2: 使用 zrevrange 代替
const chats = await kv.zrevrange(`user:chat:${userId}`, 0, -1);
错误 : 启动开发服务器后,kv.get() 首次调用返回 null,后续调用返回正确数据 来源 : GitHub Issue #781 原因 : Next.js 静态渲染缓存了首次响应。在 Next.js 开发服务器每次编译重启时都会发生。预防 : 使用 unstable_noStore() 强制动态渲染,在 fetch 调用中使用 cache: 'no-store',或添加重试逻辑。
import { unstable_noStore as noStore } from 'next/cache';
export async function getData() {
noStore(); // 强制动态渲染
const data = await kv.get('mykey');
return data; // 现在首次调用返回正确值
}
// 或添加重试逻辑
async function getWithRetry(key: string, retries = 2) {
let data = await kv.get(key);
let attempt = 0;
while (!data && attempt < retries) {
await new Promise(r => setTimeout(r, 100));
data = await kv.get(key);
attempt++;
}
return data;
}
错误 : 在 Vite 中导入 @vercel/kv 时出现 Uncaught ReferenceError: process is not defined 来源 : GitHub Issue #743 原因 : Vite 默认不填充 Node.js 的 process.env。@vercel/kv 期望 process.env 存在。预防 : 使用 Vite 的 define 进行填充,使用 createClient 配合 import.meta.env,或安装 vite-plugin-node-polyfills。
// 选项 1: 使用 define 的 Vite 配置
// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
define: {
'process.env.KV_REST_API_URL': JSON.stringify(env.KV_REST_API_URL),
'process.env.KV_REST_API_TOKEN': JSON.stringify(env.KV_REST_API_TOKEN),
},
};
});
// 选项 2: 使用 createClient 配合 import.meta.env
import { createClient } from '@vercel/kv';
const kv = createClient({
url: import.meta.env.VITE_KV_REST_API_URL,
token: import.meta.env.VITE_KV_REST_API_TOKEN,
});
错误 : 服务器操作中的 kv.get() 即使在 KV 值更新后也返回陈旧/缓存的数据 来源 : GitHub Issue #510 原因 : Next.js 静态渲染缓存了服务器操作响应。控制台输出显示为黄色(缓存命中指示器)。预防 : 使用 unstable_noStore() 强制动态渲染,或使用路由处理器代替服务器操作。
// 在服务器操作中
'use server'
import { unstable_noStore as noStore } from 'next/cache';
export async function logChat(text: string) {
noStore(); // 强制动态渲染
let n_usage = await kv.get('n_usage');
// 现在返回新值,而不是缓存值
}
// 或使用路由处理器(自动动态)
// app/api/chat/route.ts
export async function GET() {
let n_usage = await kv.get('n_usage'); // 新数据
return Response.json({ n_usage });
}
游标类型更改 (Release Notes):
扫描游标现在返回 string 而不是 number
将比较从 cursor !== 0 更新为 cursor !== "0"
// v2.x let cursor: number = 0; do { const [newCursor, keys] = await kv.scan(cursor); cursor = newCursor; } while (cursor !== 0);
// v3.x let cursor: string = "0"; do { const [newCursor, keys] = await kv.scan(cursor); cursor = newCursor; } while (cursor !== "0");
默认启用自动管道化 (Release Notes):
命令现在自动批量处理以提高性能
在边缘情况下可能导致时序问题
如果需要,可通过 enableAutoPipelining: false 禁用
// 如果自动管道化导致问题,禁用它: import { createClient } from '@vercel/kv';
const kv = createClient({ url: process.env.KV_REST_API_URL, token: process.env.KV_REST_API_TOKEN, enableAutoPipelining: false // 禁用自动管道化 });
引入了 scanIterator() 错误 : v2.0.0+ 存在 scanIterator() 的无限循环错误。解决方法请参见问题 #11。
@vercel/kv 不支持 Redis Streams(XREAD、XADD、XGROUP 等),即使底层的 Upstash Redis 支持它们。该包缺少流方法。
解决方法 : 对于流,直接使用 @upstash/redis:
// @vercel/kv 没有流方法
// await kv.xAdd(...) // TypeError: kv.xAdd is not a function
// 直接使用 Upstash Redis 客户端
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.KV_REST_API_URL!,
token: process.env.KV_REST_API_TOKEN!,
});
await redis.xadd('stream:events', '*', { event: 'user.login' });
const messages = await redis.xread('stream:events', '0');
来源 : GitHub Issue #674
kv.hgetall() 返回一个 JavaScript 对象,而不是 JSON 字符串。这会让期望字符串序列化的开发者感到困惑。
await kv.hset('foobar', { '1834': 'https://example.com' });
const data = await kv.hgetall('foobar');
console.log(typeof data); // "object" 而不是 "string"
// 它已经是一个对象 - 直接使用
console.log(data['1834']); // 'https://example.com'
// 如果需要 JSON 字符串
const jsonString = JSON.stringify(data);
分布式锁(防止竞态条件):
const lockKey = `lock:${resource}`;
const lockValue = crypto.randomUUID();
const acquired = await kv.setnx(lockKey, lockValue);
if (acquired) {
await kv.expire(lockKey, 10); // TTL 防止死锁
try {
await processOrders();
} finally {
const current = await kv.get(lockKey);
if (current === lockValue) await kv.del(lockKey);
}
}
排行榜(有序集合):
await kv.zadd('leaderboard', { score, member: userId.toString() });
// 注意:带 { rev: true } 的 zrange 存在已知错误(问题 #12)
// 为了可靠性,请改用 zrevrange
const top = await kv.zrevrange('leaderboard', 0, 9, { withScores: true });
const rank = await kv.zrevrank('leaderboard', userId.toString());
最后验证 : 2026-01-21 | 技能版本 : 2.0.0 | 变更 : 新增 5 个问题(scanIterator 挂起、zrange rev 错误、Next.js 开发服务器 null、Vite process 错误、服务器操作陈旧缓存),扩展了问题 #1、#2、#7、#9,包含 TIER 1-2 研究成果,新增了版本迁移指南和已知限制部分
每周安装量
314
仓库
GitHub 星标
643
首次出现
2026年1月20日
安全审计
安装于
claude-code260
gemini-cli211
opencode208
cursor199
antigravity194
codex185
Last Updated : 2026-01-21 Version : @vercel/kv@3.0.0 (Redis-compatible, powered by Upstash)
# Create KV: Vercel Dashboard → Storage → KV
vercel env pull .env.local # Creates KV_REST_API_URL and KV_REST_API_TOKEN
npm install @vercel/kv
Basic Usage :
import { kv } from '@vercel/kv';
// Set with TTL (expires in 1 hour)
await kv.setex('session:abc', 3600, { userId: 123 });
// Get
const session = await kv.get('session:abc');
// Increment counter (atomic)
const views = await kv.incr('views:post:123');
CRITICAL : Always use namespaced keys (user:123 not 123) and set TTL for temporary data.
Caching (cache-aside):
const cached = await kv.get(`post:${slug}`);
if (cached) return cached;
const post = await db.query.posts.findFirst({ where: eq(posts.slug, slug) });
await kv.setex(`post:${slug}`, 3600, post); // Cache 1 hour
return post;
Rate Limiting :
async function checkRateLimit(ip: string): Promise<boolean> {
const key = `ratelimit:${ip}`;
const current = await kv.incr(key);
if (current === 1) await kv.expire(key, 60); // 60s window
return current <= 10; // 10 requests per window
}
Session Management :
const sessionId = crypto.randomUUID();
await kv.setex(`session:${sessionId}`, 7 * 24 * 3600, { userId });
Pipeline (batch operations):
const pipeline = kv.pipeline();
pipeline.set('user:1', data);
pipeline.incr('counter');
const results = await pipeline.exec(); // Single round-trip
Key Naming : Use namespaces like user:123, post:abc:views, ratelimit:ip:endpoint
Always :
setex not set)user:123 not 123)Never :
This skill prevents 15 documented issues :
Error : Error: KV_REST_API_URL is not defined or KV_REST_API_TOKEN is not defined Source : https://vercel.com/docs/storage/vercel-kv/quickstart | GitHub Issue #759 Why It Happens : Environment variables not set locally or in deployment. In monorepos (Turborepo/pnpm workspaces), abstracting @vercel/kv into a shared package can cause Vercel builds to fail even though local builds work. Prevention : Run vercel env pull .env.local and ensure .env.local is in .gitignore. For monorepos, either (1) create client in consuming app not shared package, (2) use Vercel Environment Variables UI to set at project level, or (3) add env vars to turbo.json pipeline config: { "pipeline": { "build": { "env": ["KV_REST_API_URL", "KV_REST_API_TOKEN"] } } }.
Error : TypeError: Do not know how to serialize a BigInt or circular reference errors. Also, hset() coerces numeric strings to numbers. Source : https://github.com/vercel/storage/issues/89 | GitHub Issue #727 Why It Happens : Trying to store non-JSON-serializable data (functions, BigInt, circular refs). Additionally, when using hset() to store string values that look numeric (e.g., '123456'), hgetall() returns them as numbers, breaking type consistency. Prevention : Only store plain objects, arrays, strings, numbers, booleans, null. Convert BigInt to string. For hash fields with numeric strings, either (1) use non-numeric prefix like 'code_123456', (2) store as JSON string and parse after retrieval, or (3) validate and recast types: String(value.field) after .
Error : Unexpected data returned, data overwritten by different feature Source : Production debugging, best practices Why It Happens : Using generic key names like cache, data, temp across different features Prevention : Always use namespaced keys: feature:id:type pattern.
Error : Memory usage grows indefinitely, old data never expires Source : Vercel KV best practices Why It Happens : Using set() without setex() for temporary data Prevention : Use setex(key, ttl, value) for all temporary data. Set appropriate TTL (seconds).
Error : Error: Rate limit exceeded or commands failing Source : https://vercel.com/docs/storage/vercel-kv/limits Why It Happens : Exceeding 30,000 commands/month on free tier Prevention : Monitor usage in Vercel dashboard, upgrade plan if needed, use caching to reduce KV calls.
Error : Error: Value too large or performance degradation Source : https://vercel.com/docs/storage/vercel-kv/limits Why It Happens : Trying to store values >1MB in KV Prevention : Use Vercel Blob for files/images. Keep KV values small (<100KB recommended).
Error : TypeScript errors, runtime type errors. Generic kv.get<T>() sometimes returns null even when data exists. Source : Common TypeScript issue | GitHub Issue #510 Why It Happens : kv.get() returns unknown type, need to cast or validate. Additionally, there's a type inference bug where using generics like kv.get<T>() can cause the function to return null even when CLI shows data exists, due to serialization/deserialization issues. Prevention : Don't use generics with get(). Instead, retrieve without type parameter and cast after retrieval: const rawData = await kv.get('key'); const data = rawData as MyType | null;. Validate with Zod or type guards before using.
Error : Silent failures, partial execution Source : https://github.com/vercel/storage/issues/120 Why It Happens : Pipeline execution can have individual command failures Prevention : Check results array from pipeline.exec() and handle errors.
Error : Slow queries, timeout errors. In v3.0.0+, cursor type changed from number to string. Source : Redis best practices | Release Notes v3.0.0 Why It Happens : Using scan() with large datasets or wrong cursor handling. Version 3.0.0 introduced a breaking change where scan cursor is now string instead of number. Prevention : Limit count parameter, iterate properly with cursor, avoid full scans in production. In v3.0.0+, use let cursor: string = "0" and compare with cursor !== "0" (not !== 0).
Error : Session expires too early, cache invalidates prematurely Source : Production debugging Why It Happens : Not refreshing TTL on access (sliding expiration) Prevention : Use expire(key, newTTL) on access to implement sliding windows.
Error : for await loop never terminates when using kv.scanIterator() Source : GitHub Issue #706 Why It Happens : Bug in v2.0.0+ where iterator doesn't properly signal completion. The iterator processes keys correctly but never exits, preventing the function from returning. Also affects sscanIterator(). Prevention : Use manual scan() with cursor instead of scanIterator().
// Don't use scanIterator() - it hangs in v2.0.0+
for await (const key of kv.scanIterator()) {
// This loop never terminates
}
// Use manual scan with cursor instead
let cursor: string = "0"; // v3.x uses string
do {
const [newCursor, keys] = await kv.scan(cursor);
cursor = newCursor;
for (const key of keys) {
const value = await kv.get(key);
// process key/value
}
} while (cursor !== "0");
Error : kv.zrange(key, 0, -1, { rev: true }) returns empty array even though data exists Source : GitHub Issue #742 Why It Happens : SDK bug in reverse flag handling for certain key patterns. CLI always returns correct values. Removing the rev flag returns data correctly. Prevention : Omit rev flag and reverse in-memory, or use zrevrange() instead.
// This sometimes returns empty array (BUG)
const chats = await kv.zrange(`user:chat:${userId}`, 0, -1, { rev: true });
// [] - but CLI shows 12 items
// Workaround 1: Omit rev flag and reverse in-memory
const chats = await kv.zrange(`user:chat:${userId}`, 0, -1);
const reversedChats = chats.reverse();
// Workaround 2: Use zrevrange instead
const chats = await kv.zrevrange(`user:chat:${userId}`, 0, -1);
Error : kv.get() returns null on first call after starting dev server, correct data on subsequent calls Source : GitHub Issue #781 Why It Happens : Next.js static rendering caches the first response. Happens on each compilation restart in Next.js dev server. Prevention : Force dynamic rendering with unstable_noStore(), use cache: 'no-store' in fetch calls, or add retry logic.
import { unstable_noStore as noStore } from 'next/cache';
export async function getData() {
noStore(); // Force dynamic rendering
const data = await kv.get('mykey');
return data; // Now returns correct value on first call
}
// Or add retry logic
async function getWithRetry(key: string, retries = 2) {
let data = await kv.get(key);
let attempt = 0;
while (!data && attempt < retries) {
await new Promise(r => setTimeout(r, 100));
data = await kv.get(key);
attempt++;
}
return data;
}
Error : Uncaught ReferenceError: process is not defined when importing @vercel/kv in Vite Source : GitHub Issue #743 Why It Happens : Vite doesn't polyfill Node.js process.env by default. @vercel/kv expects process.env to exist. Prevention : Use Vite's define to polyfill, use createClient with import.meta.env, or install vite-plugin-node-polyfills.
// Option 1: Vite config with define
// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
define: {
'process.env.KV_REST_API_URL': JSON.stringify(env.KV_REST_API_URL),
'process.env.KV_REST_API_TOKEN': JSON.stringify(env.KV_REST_API_TOKEN),
},
};
});
// Option 2: Use createClient with import.meta.env
import { createClient } from '@vercel/kv';
const kv = createClient({
url: import.meta.env.VITE_KV_REST_API_URL,
token: import.meta.env.VITE_KV_REST_API_TOKEN,
});
Error : kv.get() in Server Actions returns stale/cached data even after KV values are updated Source : GitHub Issue #510 Why It Happens : Next.js static rendering caches Server Action responses. Console output appears yellow (cache hit indicator). Prevention : Use unstable_noStore() to force dynamic rendering, or use route handlers instead of Server Actions.
// In Server Actions
'use server'
import { unstable_noStore as noStore } from 'next/cache';
export async function logChat(text: string) {
noStore(); // Force dynamic rendering
let n_usage = await kv.get('n_usage');
// Now returns fresh value, not cached
}
// Or use route handlers (automatically dynamic)
// app/api/chat/route.ts
export async function GET() {
let n_usage = await kv.get('n_usage'); // Fresh data
return Response.json({ n_usage });
}
Cursor Type Changed (Release Notes):
Scan cursor now returns string instead of number
Update comparisons from cursor !== 0 to cursor !== "0"
// v2.x let cursor: number = 0; do { const [newCursor, keys] = await kv.scan(cursor); cursor = newCursor; } while (cursor !== 0);
// v3.x let cursor: string = "0"; do { const [newCursor, keys] = await kv.scan(cursor); cursor = newCursor; } while (cursor !== "0");
Auto-Pipelining Enabled by Default (Release Notes):
Commands now automatically batched for performance
May cause timing issues in edge cases
Disable with enableAutoPipelining: false if needed
// If auto-pipelining causes issues, disable it: import { createClient } from '@vercel/kv';
const kv = createClient({ url: process.env.KV_REST_API_URL, token: process.env.KV_REST_API_TOKEN, enableAutoPipelining: false // Disable auto-pipelining });
scanIterator() Bug Introduced : v2.0.0+ has infinite loop bug with scanIterator(). See Issue #11 for workaround.
Source : Vercel KV Redis Compatibility | GitHub Issue #278
@vercel/kv does not support Redis Streams (XREAD, XADD, XGROUP, etc.) even though underlying Upstash Redis supports them. The package lacks stream methods.
Workaround : Use @upstash/redis directly for streams:
// @vercel/kv doesn't have stream methods
// await kv.xAdd(...) // TypeError: kv.xAdd is not a function
// Use Upstash Redis client directly
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.KV_REST_API_URL!,
token: process.env.KV_REST_API_TOKEN!,
});
await redis.xadd('stream:events', '*', { event: 'user.login' });
const messages = await redis.xread('stream:events', '0');
Source : GitHub Issue #674
kv.hgetall() returns a JavaScript object, not a JSON string. This confuses developers expecting string serialization.
await kv.hset('foobar', { '1834': 'https://example.com' });
const data = await kv.hgetall('foobar');
console.log(typeof data); // "object" not "string"
// It's already an object - use directly
console.log(data['1834']); // 'https://example.com'
// If you need JSON string
const jsonString = JSON.stringify(data);
Distributed Lock (prevents race conditions):
const lockKey = `lock:${resource}`;
const lockValue = crypto.randomUUID();
const acquired = await kv.setnx(lockKey, lockValue);
if (acquired) {
await kv.expire(lockKey, 10); // TTL prevents deadlock
try {
await processOrders();
} finally {
const current = await kv.get(lockKey);
if (current === lockValue) await kv.del(lockKey);
}
}
Leaderboard (sorted sets):
await kv.zadd('leaderboard', { score, member: userId.toString() });
// Note: zrange with { rev: true } has a known bug (Issue #12)
// Use zrevrange instead for reliability
const top = await kv.zrevrange('leaderboard', 0, 9, { withScores: true });
const rank = await kv.zrevrank('leaderboard', userId.toString());
Last verified : 2026-01-21 | Skill version : 2.0.0 | Changes : Added 5 new issues (scanIterator hang, zrange rev bug, Next.js dev server null, Vite process error, Server Actions stale cache), expanded Issues #1, #2, #7, #9 with TIER 1-2 research findings, added Version Migration Guide and Known Limitations sections
Weekly Installs
314
Repository
GitHub Stars
643
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code260
gemini-cli211
opencode208
cursor199
antigravity194
codex185
Supabase Postgres 最佳实践指南 - 8大类别性能优化规则与SQL示例
57,300 周安装
2025漏洞扫描器指南:OWASP Top 10、供应链安全与风险优先级排序
466 周安装
Context7自动研究技能:为Claude Code自动获取最新库框架文档
466 周安装
简明规划技能:AI辅助项目规划工具,生成可执行原子化步骤计划
467 周安装
App Builder 应用构建编排器:AI 智能规划技术栈、项目结构与多代理协调
468 周安装
阿里云Data Lake Formation管理指南:使用OpenAPI和SDK管理数据湖资源
239 周安装
Excel-CLI 命令行工具:Windows Excel 自动化脚本与批处理操作指南
469 周安装
hgetall()