Catalog
github/upstash-redis

github

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.

v1.0LATEST
NewUpdated Sep 2, 2026

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

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 getUser returns without hitting the database and await 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: getSession after createSession returns the object with userId; after destroySession it returns null.

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 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

Files1
1 files · 1.0 KB

Select a file to preview

Overall Score

87/100

Grade

A

Excellent

Grades are signals, not a certification. Always review a skill yourself before use.

Safety

88

Quality

86

Clarity

89

Completeness

82

Summary

This skill teaches developers how to use Upstash Redis (HTTP-based Redis client) and rate limiting in serverless environments. It provides step-by-step guidance on installation, caching with TTL, session management with sliding expiration, and rate limiting in route handlers, with checkpoints and common pitfalls documented.

Detected Capabilities

environment-variable-readhttp-client-usagecode-generationtypescript-examplesapi-integration

Trigger Keywords

Phrases that agents use to match this skill to user intent.

upstash redisserverless cachingrate limiting apisession management redisedge middleware cachehttp redis clientsliding window rate limit

Risk Signals

INFO

UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN environment variables required

Requirements and limitations section
INFO

Credentials stored as environment variables with Redis.fromEnv() pattern

Step 1 — Install and create one client per module
WARNING

IP address extracted from x-forwarded-for header without validation guardrails documented

Step 4 — Rate limiting a route handler
INFO

Guidance explicitly warns against trusting x-forwarded-for blindly, recommends first-hop extraction

Common pitfalls section

Referenced Domains

External domains referenced in skill content, detected by static analysis.

github.comupstash.com

Use Cases

  • Add HTTP-based Redis caching to serverless applications
  • Implement session management with automatic sliding expiration
  • Add rate limiting to API routes and edge middleware
  • Cache database queries with time-to-live in Next.js or Cloudflare Workers
  • Rate limit user actions by IP or authenticated user identity

Quality Notes

  • Strengths: Clear step-by-step progression with checkpoints after each section; practical code examples covering three core use cases (caching, sessions, rate limiting); well-documented limitations (TCP Redis, vector search, in-memory caching); explicit 'When NOT to use' section; comprehensive common pitfalls section addressing serialization, TTL, and x-forwarded-for issues
  • Strengths: Environment variable pattern follows best practice (credentials from env, not hardcoded); namespace guidance for cache keys (user:id, session:id) prevents collisions
  • Strengths: Algorithm choices explained (fixedWindow vs slidingWindow vs tokenBucket) with performance/accuracy tradeoffs; edge runtime guidance for analytics pending promises and context.waitUntil
  • Minor areas for improvement: x-forwarded-for extraction code could be slightly more defensive (e.g., explicit trim examples); no guidance on error handling for network-level failures (Upstash API downtime); rate limiting identifier selection (user id vs IP) lacks recommendation tree for different scenarios
Model: claude-haiku-4-5-20251001Analyzed: Sep 2, 2026

Reviews

Add this skill to your library to leave a review.

No reviews yet

Be the first to share your experience.

Use github/upstash-redis in your dev environment

Command Palette

Search for a command to run...