Claude Code for Upstash Redis — Workflow Guide
The Setup
You are adding serverless Redis for caching, rate limiting, or session storage using Upstash. Unlike traditional Redis that needs a persistent connection, Upstash works over HTTP — making it ideal for serverless environments like Vercel, Cloudflare Workers, and AWS Lambda. Claude Code can set up caching patterns and rate limiters, but it defaults to the wrong Redis client and connection model.
What Claude Code Gets Wrong By Default
-
Uses the
redisorioredisnpm package. Claude installs the standard Redis client that needs TCP connections. Upstash requires@upstash/rediswhich communicates over HTTP REST, working in serverless where TCP connections are not available. -
Creates persistent connection pools. Claude writes
createClient()with connection pooling logic. Upstash is stateless HTTP — every request is independent. There are no connections to pool or manage. -
Misses the REST token authentication. Claude tries to connect with
redis://host:portURLs. Upstash usesUPSTASH_REDIS_REST_URLandUPSTASH_REDIS_REST_TOKEN— a URL and bearer token pair, not a connection string. -
Ignores Upstash-specific rate limiting. Claude builds custom rate limiters with MULTI/EXEC transactions. Upstash has
@upstash/ratelimitwith pre-built sliding window, fixed window, and token bucket algorithms.
The CLAUDE.md Configuration
# Upstash Redis Serverless Project
## Architecture
- Cache: Upstash Redis (HTTP-based, serverless)
- Client: @upstash/redis (NOT ioredis or redis packages)
- Rate limiting: @upstash/ratelimit
- Framework: Next.js on Vercel (serverless functions)
## Upstash Rules
- Use Redis.fromEnv() to initialize from UPSTASH_REDIS_REST_URL/TOKEN
- All operations are async/await (HTTP calls, not TCP)
- No connection management needed — stateless HTTP
- Use pipeline() for batching multiple commands in one request
- TTL is in seconds for set() with { ex: seconds } option
- Rate limiter: new Ratelimit({ redis, limiter: Ratelimit.slidingWindow() })
## Conventions
- Redis client singleton in lib/redis.ts
- Cache keys use colon namespacing: "user:123:profile"
- Rate limiter in lib/ratelimit.ts
- All cached data has explicit TTL — never cache without expiration
- Never store secrets or tokens in Redis
- Use JSON.stringify/parse for object storage
Workflow Example
You want to add API rate limiting to protect your endpoints. Prompt Claude Code:
“Add rate limiting to the /api/generate endpoint using Upstash Ratelimit. Allow 10 requests per 10-second sliding window per IP address. Return a 429 with retry-after header when limited.”
Claude Code should create a rate limiter instance using @upstash/ratelimit with Ratelimit.slidingWindow(10, '10 s'), check the limit using the client IP as the identifier, and return appropriate headers including X-RateLimit-Remaining and Retry-After when the limit is exceeded.
Common Pitfalls
-
Not using pipeline for multi-step operations. Claude makes individual Redis calls in loops. Upstash charges per request and each is an HTTP round-trip. Use
redis.pipeline()to batch commands into a single HTTP request. -
Serialization assumptions. Claude stores objects with
redis.set('key', myObject). Upstash auto-serializes to JSON, but when reading back, Claude forgets that the data is already parsed — no need for an extraJSON.parse()on the result. -
Rate limiter identifier collisions. Claude uses
req.ipdirectly, but behind Vercel’s proxy this can be undefined. Usereq.headers.get('x-forwarded-for')or Vercel’sreq.ipwith a fallback to avoid null identifiers that share a single rate limit bucket.