Skip to main content

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.

소스 정보

저장소
github/awesome-copilot
최근 소스 활동
2026년 9월 2일 02:11
감지된 SKILL.md 언어
영어
스타
40,063
포크
5,129

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
upstash-redis
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.
license
MIT
compatibility
@upstash/redis 1.x, @upstash/ratelimit 2.x, Node.js 18+ or any runtime with global fetch
metadata
{"author":"Upstash"}
# 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_URL` and `UPSTASH_REDIS_REST_TOKEN`. - Every command is an HTTP request. Batch with `pipeline()` or `MGET`/`MSET` when you issue many commands per request; avoid `KEYS *` in production. - Values are serialized automatically (objects, arrays, numbers round-trip). Do not `JSON.stringify` before `set` or `parseInt` after `get`. ## Step 1 — Install and create one client per module ```bash npm install @upstash/redis @upstash/ratelimit ``` ```ts // 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 ```ts 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 `getUser` returns without hitting the database > and `await redis.ttl("user:123")` is positive. ## Step 3 — Sessions with sliding expiration ```ts 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**: `getSession` after `createSession` returns the object with > `userId`; after `destroySession` it returns `null`. ## Step 4 — Rate limiting a route handler ```ts // 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 accept `ms`, `s`, `m`, `h`, `d`. - Tiers: create one `Ratelimit` per tier with different `prefix` values. - Edge middleware / Cloudflare Workers with `analytics: true`: the result has a `pending` promise; pass it to `context.waitUntil(pending)` so background work finishes before the runtime exits. - `reset` is a Unix timestamp in milliseconds. > **Checkpoint**: the 11th request within 10 seconds returns 429 with a > `Retry-After` header; after the window it succeeds again. ## Common pitfalls - **Creating clients inside handlers**: the limiter's in-memory `ephemeralCache` only helps when the instance outlives the request. - **Manual JSON**: `redis.set("k", JSON.stringify(v))` then `redis.get` returns an already-parsed object; double parsing throws. - **No TTL on cache keys**: memory grows until eviction; always pass `{ ex }`. - **Trusting `x-forwarded-for` blindly**: take the first hop, or use the platform's IP helper, when behind a proxy. - **Forgetting `pending`** on 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 - https://upstash.com/docs/redis/sdks/ts/overview - https://upstash.com/docs/redis/sdks/ratelimit-ts/overview - https://github.com/upstash/redis-js - https://github.com/upstash/ratelimit-js
GitHub에서 보기