upstash-redis
Use Redis over HTTP from serverless and edge runtimes with @upstash/redis, and add rate limiting with @upstash/ratelimit. Use when the user mentions Upstash Redis, needs Redis from a Next.js route handler or middleware, Vercel, Cloudflare Workers, Deno, or Bun without TCP connection pooling, or wants cache-aside with TTLs, a session store, counters, or a 429 rate limiter using fixed window, sliding window, or token bucket. DO NOT use for self-hosted or TCP Redis clients (ioredis, node-redis), Redis Cluster administration, or vector similarity search.
- License 许可
- MIT
- 最近更新
- 2026-11-11
- Stars
- ★ 4.1w
- Forks
- 5.3k
01 前言 Front Matter
- name
- upstash-redis
- license
- MIT
- metadata
- { "author": "Upstash" }
- description
- Use Redis over HTTP from serverless and edge runtimes with @upstash/redis, and add rate limiting with @upstash/ratelimit. Use when the user mentions Upstash Redis, needs Redis from a Next.js route handler or middleware, Vercel, Cloudflare Workers, Deno, or Bun without TCP connection pooling, or wants cache-aside with TTLs, a session store, counters, or a 429 rate limiter using fixed window, sliding window, or token bucket. DO NOT use for self-hosted or TCP Redis clients (ioredis, node-redis), Redis Cluster administration, or vector similarity search.
- compatibility
- @upstash/redis 1.x, @upstash/ratelimit 2.x, Node.js 18+ or any runtime with global fetch
03 SKILL.md 正文
Upstash Redis Skill
This skill covers the three things serverless apps most often need Redis for: caching, sessions, and rate limiting. The client talks to Redis over HTTP, so it works where a long-lived TCP connection does not (edge middleware, short lived functions). Follow the steps in order; each ends with a checkpoint.
Requirements and limitations
- An Upstash Redis database (hosted service; usage-based pricing with a free tier). Credentials are a REST URL and token from the database page.
- Environment variables
UPSTASH_REDIS_REST_URLandUPSTASH_REDIS_REST_TOKEN. - Every command is an HTTP request. Batch with
pipeline()orMGET/MSETwhen you issue many commands per request; avoidKEYS *in production. - Values are serialized automatically (objects, arrays, numbers round-trip).
Do not
JSON.stringifybeforesetorparseIntafterget.
Step 1 — Install and create one client per module
npm install @upstash/redis @upstash/ratelimit
// lib/redis.ts
import { Redis } from "@upstash/redis";
// Reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN
export const redis = Redis.fromEnv();
Create the client at module scope, not inside the request handler, so ephemeral caches and pipelines can be reused across invocations.
Checkpoint:
await redis.ping()returns"PONG".
Step 2 — Cache-aside with TTL
import { redis } from "@/lib/redis";
type User = { id: string; name: string; plan: "free" | "pro" };
export async function getUser(userId: string): Promise<User | null> {
const key = `user:${userId}`;
const cached = await redis.get<User>(key);
if (cached) return cached;
const user = await db.users.findById(userId); // your data source
if (user) await redis.set(key, user, { ex: 3600 }); // 1 hour TTL
return user;
}
export async function updateUser(userId: string, patch: Partial<User>) {
const user = await db.users.update(userId, patch);
await redis.set(`user:${userId}`, user, { ex: 3600 }); // write-through
return user;
}
export async function deleteUser(userId: string) {
await db.users.delete(userId);
await redis.del(`user:${userId}`); // invalidate
}
Always set a TTL on cache entries; namespace keys (user:123, session:abc).
Checkpoint: second call to
getUserreturns without hitting the database andawait redis.ttl("user:123")is positive.
Step 3 — Sessions with sliding expiration
import { redis } from "@/lib/redis";
const SESSION_TTL = 60 * 60 * 24; // 24 hours
export async function createSession(userId: string, data: Record<string, unknown>) {
const sessionId = crypto.randomUUID();
await redis.set(`session:${sessionId}`, { userId, ...data, createdAt: Date.now() }, { ex: SESSION_TTL });
return sessionId;
}
export async function getSession<T = Record<string, unknown>>(sessionId: string) {
const session = await redis.get<T>(`session:${sessionId}`);
if (session) await redis.expire(`session:${sessionId}`, SESSION_TTL); // slide
return session;
}
export async function destroySession(sessionId: string) {
await redis.del(`session:${sessionId}`);
}
Store the session id in an HttpOnly; Secure; SameSite cookie; never put the
Redis token in client code.
Checkpoint:
getSessionaftercreateSessionreturns the object withuserId; afterdestroySessionit returnsnull.
Step 4 — Rate limiting a route handler
// app/api/search/route.ts (Next.js App Router; same pattern for any fetch handler)
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"), // 10 requests per 10 seconds
prefix: "ratelimit:search", // isolate keys per limiter
});
export async function POST(request: Request) {
const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "anonymous";
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
if (!success) {
return new Response("Too Many Requests", {
status: 429,
headers: {
"X-RateLimit-Limit": String(limit),
"X-RateLimit-Remaining": String(remaining),
"Retry-After": String(Math.max(0, Math.ceil((reset - Date.now()) / 1000))),
},
});
}
// handle the request
return Response.json({ ok: true });
}
- Identifier: use the user id or API key when authenticated; fall back to IP.
- Algorithms:
Ratelimit.fixedWindow(n, "1 m")(cheapest),slidingWindow(smooth boundaries, default choice),tokenBucket(refill, "10 s", max)(allows bursts). Windows acceptms,s,m,h,d. - Tiers: create one
Ratelimitper tier with differentprefixvalues. - Edge middleware / Cloudflare Workers with
analytics: true: the result has apendingpromise; pass it tocontext.waitUntil(pending)so background work finishes before the runtime exits. resetis a Unix timestamp in milliseconds.
Checkpoint: the 11th request within 10 seconds returns 429 with a
Retry-Afterheader; after the window it succeeds again.
Common pitfalls
- Creating clients inside handlers: the limiter's in-memory
ephemeralCacheonly helps when the instance outlives the request. - Manual JSON:
redis.set("k", JSON.stringify(v))thenredis.getreturns an already-parsed object; double parsing throws. - No TTL on cache keys: memory grows until eviction; always pass
{ ex }. - Trusting
x-forwarded-forblindly: take the first hop, or use the platform's IP helper, when behind a proxy. - Forgetting
pendingon edge runtimes with analytics or multi-region limiters.
When NOT to use this skill
- Long-running servers with a TCP Redis connection already in place: keep ioredis/node-redis.
- Vector search or RAG: use a vector database skill instead.
- Sub-millisecond, in-process caching: use an in-memory LRU.
References
04 同仓库更多 Skills
supabase-rls-policies
Write, review, and test Supabase Row Level Security (RLS) policies so that one user cannot read or change another user's rows. Use when creating or changing tables, policies, views, or security definer functions in a Supabase project, when reviewing a migration that touches RLS, or when asked to prove user isolation with pgTAP tests that sign in as two users.
skill-distribution-audit
Audit a skill collection before release or investigate installer omissions, archived/template leakage, duplicate identities, and manifest drift. Reconcile the intended public source set, distribution manifest, and actual discovery output for a specified consumer version. Includes a deterministic set-comparison helper.
logicapps-custom-code-net10-migration
Migrate Azure Logic Apps Standard local-function custom-code projects from .NET 8 to .NET 10. Use for legacy Logic Apps custom-code workspace upgrades, project/package configuration, custom-worker settings, publication, and integrated VS Code debugging. Not for ordinary .NET applications, standalone Azure Functions, Consumption workflows, or cloud deployment.
winmd-api-search
Find and explore Windows desktop APIs. Use when building features that need platform capabilities — camera, file access, notifications, UI controls, AI/ML, sensors, networking, etc. Discovers the right API for a task and retrieves full type details (methods, properties, events, enumeration values).
resemble-detect
Deepfake detection and media safety — detect AI-generated audio, images, video, and text, trace synthesis sources, apply watermarks, verify speaker identity, and analyze media intelligence using Resemble AI
quality-playbook
Run a complete quality engineering audit on any codebase. Derives behavioral requirements from the code, generates spec-traced functional tests, runs a three-pass code review with regression tests, executes a multi-model spec audit (Council of Three), and produces a consolidated bug report with TDD-verified patches. Finds the 35% of real defects that structural code review alone cannot catch. Works with any language. Trigger on 'quality playbook', 'spec audit', 'Council of Three', 'fitness-to-purpose', or 'coverage theater'.