Upstash Redis
GitHubOfficialCreated by UpstashDistributed by GitHub
- Skill
- Tool integration
- Cloud and DevOps
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a guide for implementing Redis-based caching, session management, and rate limiting in serverless and edge environments. By using the @upstash/redis and @upstash/ratelimit libraries, developers can interact with Redis over HTTP, bypassing the limitations of traditional TCP connections in ephemeral runtimes like Vercel, Cloudflare Workers, Deno, or Bun. The guide covers client initialization, cache-aside patterns with TTL, session handling with sliding expiration, and route-level rate limiting.
What it does
- Initializes Upstash Redis clients for serverless and edge environments.
- Implements cache-aside patterns with automatic TTL management.
- Manages user sessions with sliding expiration logic.
- Configures route-level rate limiting using fixed window, sliding window, or token bucket algorithms.
- Provides guidance on handling HTTP-based Redis communication without manual serialization.
- Offers best practices for avoiding common pitfalls like client re-initialization and missing TTLs.
When to use it
- When deploying to serverless or edge runtimes where persistent TCP connections are not supported.
- When you need to implement caching, session storage, or rate limiting in Next.js, Vercel, or Cloudflare Workers.
- When you require a managed Redis service with usage-based pricing.
- When you need to protect API routes from abuse using rate limiting.
Advantages
- Enables Redis usage in environments that do not support long-lived TCP connections.
- Simplifies data handling with automatic serialization of objects and numbers.
- Provides flexible rate-limiting algorithms suitable for various traffic patterns.
- Supports efficient resource usage through pipeline and batching commands.
Usage examples
- “How do I implement a cache-aside pattern for user data in a Next.js route handler using Upstash?”
- The agent provides a code example using redis.get and redis.set with a TTL, demonstrating how to check the cache before querying the database.
- “I need to rate limit my API route to 10 requests per 10 seconds. How can I do this with Upstash?”
- The agent provides a snippet using the Ratelimit class with the slidingWindow algorithm, configured to use the request IP as the identifier.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/github/upstash-redis/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip upstash-redis.zip -d .agents/skills
Documentation by 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_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
Files
- ATTRIBUTION.mdAdded by AutoGineer888 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md6.8 KiB
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an API key, Needs Node.js
The author says: @upstash/redis 1.x, @upstash/ratelimit 2.x, Node.js 18+ or any runtime with global fetch
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Upstash
- Publisher
- GitHub (Official)
- Distributed by
- GitHub
- Source
- github/awesome-copilot · skills/upstash-redis · commit 797337f
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Upstash or GitHub.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download upstash-redis.zip