关于 api-database-vercel-kv
面向无服务器和边缘应用的 Upstash Redis 模式指南,使用基于 REST 的客户端。内容涵盖初始化、TTL、序列化、批处理、事务、速率限制与会话存储。
主要功能
- 01使用 Redis.fromEnv() 初始化客户端
- 02JSON 序列化模式
- 03TTL 与过期策略
- 04哈希操作
- 05管道与原子事务
- 06速率限制与会话存储
适用场景
- 01缓存 API 响应或查询结果
- 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 模式
快速指南: 使用
@upstash/redis(@vercel/kv的后继产品),通过 REST API 使用适用于无服务器和边缘环境的 Redis。需要注意:与 TCP Redis 相比,REST 每次调用会增加约 5-15ms 的延迟;所有值都会自动序列化为 JSON(对象可透明地往返转换,但Date对象会变成字符串);pipeline/multi 会作为单个 HTTP 请求执行,但 pipeline 并非原子操作。使用Redis.fromEnv()自动建立连接。务必设置 TTL——无服务器 Redis 按命令计费。
<critical_requirements>
关键要求:使用此技能前
所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、
import type、命名常量)
(新项目必须使用 @upstash/redis——@vercel/kv 已于 2024 年 12 月弃用,所有存储都已迁移到 Upstash Redis)
(必须为所有缓存数据设置 TTL——无服务器 Redis 按命令计费,且各套餐有存储限制)
(必须了解这是 REST/HTTP 客户端,而非 TCP Redis 客户端——每条命令都是一个 HTTP 请求,会产生约 5-15ms 的开销,因此应尽可能使用 pipeline 批处理)
</critical_requirements>
示例
- 核心模式 -- 客户端设置、CRUD 操作、TTL、哈希、pipeline、事务、速率限制、会话
其他资源:
- 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 测试分组或短期计数器
- 在 Vercel 上使用 Redis 的任何场景,只要无法使用 TCP 连接(边缘运行时)
涵盖的关键模式:
- 客户端初始化(
Redis.fromEnv()、new Redis()) - 使用自动 JSON 序列化的基本 CRUD
- TTL 和过期策略
- 用于结构化数据的哈希操作
- pipeline(批量 HTTP 请求)和事务(原子 MULTI/EXEC)
- 使用有序集合实现速率限制
- 会话存储模式
不适用场景:
- 高吞吐量、低延迟的 Redis 工作负载(使用基于 TCP 的 ioredis——REST 会增加每个请求的开销)
- Pub/Sub 订阅者(REST 是请求-响应模式,不支持持久连接)
- Redis Streams 消费者(需要 ioredis 等 TCP 客户端)
- 存储大型值(免费套餐每条记录超过 1 MB,且按命令数量计费)
- 主数据库(Redis 是缓存/临时存储,而非事实来源)
设计理念
Upstash Redis(原 Vercel KV)是一种基于 REST 的无服务器 Redis,专为无法或不便使用 TCP 连接的边缘和无服务器运行时设计。核心权衡是:在任何地方都能兼容 HTTP,但每个请求都要承担额外延迟。
核心原则:
- REST 优先 -- 每条 Redis 命令都是一个 HTTP 请求。它几乎适用于任何环境(边缘、无服务器、浏览器),但每次调用会增加约 5-15ms 的延迟。使用 pipeline 批处理。
- 自动序列化 -- 写入时将对象序列化为 JSON,读取时再反序列化。虽然方便,但这意味着
Date对象、Map、Set和函数无法被忠实保留。 - 为临时存储而设计 -- 为所有数据设置 TTL。无服务器 Redis 按命令计费,且有存储上限。应将其视为缓存,而非数据库。
- 无需管理连接 -- 无需连接池、重连逻辑或
error事件处理程序。每个请求都是无状态 HTTP 请求。
核心模式
包含良好/不良示例对的完整实现:examples/core.md
模式 1:客户端初始化
有两种方式: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())会将命令批量发送到单个 HTTP 请求中,但不具备原子性。事务(redis.multi())通过 MULTI/EXEC 提供原子性,同样只需发送单个 HTTP 请求。如果多个命令可以批量处理,请避免顺序逐个调用——每次调用都会单独进行一次 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 往返(约 5-15 毫秒)
- 存储大于 1 MB 的值——REST 请求受套餐限制(免费套餐和按量付费套餐上限为 100 MB,但大值会降低性能)
- 将 Upstash Redis 用作主数据库——它是缓存/临时存储,请始终在其他地方保留可信数据源
常见错误:
- 误以为缺失键对应的
hgetall会返回空对象{}——Upstash 返回null(而 ioredis 返回{}) - 忘记缺失键对应的
get()会返回null(而非undefined) - 传入
Date对象并期待往返后仍为Date——它们会被序列化为 ISO 字符串,取回时仍是字符串,而非Date实例
易踩坑之处与边缘情况:
automaticDeserialization: false会破坏许多 TypeScript 类型——只有在需要原始字符串响应且准备好手动处理类型时,才禁用此选项- 使用
ex选项调用set会在覆盖时重置 TTL(这是标准 Redis 行为)——如果对已有 TTL 的键调用set,新的ex值会替换原有值 - REST 延迟按请求计算,而非按命令计算——包含 10 条命令的管道与单条命令具有相同的 HTTP 开销(一次往返)
- 免费套餐每月限 500K 条命令,存储空间限 256 MB——请在生产环境中监控用量
nx(仅在键不存在时设置)失败时返回null,成功时返回"OK"——请显式检查返回值
</red_flags>
<critical_reminders>
重要提醒
所有代码都必须遵循 CLAUDE.md 中的项目规范(kebab-case、命名导出、导入顺序、
import type、命名常量)
(新项目必须使用 @upstash/redis——@vercel/kv 已于 2024 年 12 月弃用,且所有存储都已迁移至 Upstash Redis)
(所有缓存数据都必须设置 TTL——无服务器 Redis 按命令计费,并受套餐存储限制)
(必须理解这是 REST/HTTP 客户端,而非 TCP Redis 客户端——每条命令都是一个 HTTP 请求,开销约为 5-15 毫秒,因此应尽可能使用管道批量处理)
不遵守这些规则会导致使用已弃用的包、存储成本无限增长,以及无服务器函数出现不必要的延迟。
</critical_reminders>