@repo/db-upstash-redis
Redis caching, rate limiting, and session storage. Edge-compatible REST API. Cache expensive queries, limit API requests, store user sessions. Works in serverless and edge runtimes.
Quick Start
Add Redis caching in 5 minutes:
pnpm add @repo/db-upstash-redisCache database queries, rate limit API routes, store sessions. Skip to Quick Start →
Why @repo/db-upstash-redis?
Database queries are slow. Re-fetching the same data on every request wastes time and money. Users abuse APIs without rate limits. Sessions need fast storage. Traditional Redis requires persistent connections that don't work in serverless.
@repo/db-upstash-redis solves this with Upstash's REST-based Redis that works perfectly in serverless and edge environments.
Production-ready with automatic caching, rate limiting algorithms, session management, and edge runtime compatibility.
Use cases
- Query caching — Cache expensive database queries, reduce response times from 500ms to 10ms
- API rate limiting — Protect endpoints from abuse with sliding window or token bucket algorithms
- Session storage — Store user sessions with automatic expiration
- Real-time counters — Track page views, likes, votes with atomic operations
- Feature flags — Fast lookups for feature toggles without hitting the database
How it works
@repo/db-upstash-redis provides a REST-based Redis client with caching and rate limiting utilities:
import { redis } from "@repo/db-upstash-redis";
import { cache } from "@repo/db-upstash-redis/cache";
// Cache expensive query
const getUser = cache(
async (userId: string) => {
return await prisma.user.findUnique({ where: { id: userId } });
},
{ key: (userId) => `user:${userId}`, ttl: 3600 }
);
const user = await getUser("user_123"); // Cached for 1 hourUses Upstash REST API (works in edge/serverless), automatic cache invalidation, and multiple rate limiting algorithms.
Key features
Edge-compatible — REST API works in Vercel Edge, Cloudflare Workers, serverless functions
Automatic caching — Cache wrapper handles lookups, misses, and invalidation
Rate limiting — Sliding window, token bucket, and fixed window algorithms
Session storage — Built-in session management with TTL
Atomic operations — Counters, locks, and concurrent-safe updates
Pipeline support — Batch multiple operations in single network call
Quick Start
1. Install dependencies
pnpm add @repo/db-upstash-redis @upstash/redis @upstash/ratelimit2. Configure Upstash connection
UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=AXxx...3. Cache a database query
import { cache } from "@repo/db-upstash-redis/cache";
import { prisma } from "@repo/db-prisma";
export const getUser = cache(
async (userId: string) => {
return await prisma.user.findUnique({
where: { id: userId }
});
},
{
key: (userId) => `user:${userId}`,
ttl: 3600 // Cache for 1 hour
}
);
// Usage
const user = await getUser("user_123"); // Cached!4. Add rate limiting to an API route
import { ratelimit } from "@repo/db-upstash-redis/ratelimit";
const limiter = ratelimit({
requests: 10, // 10 requests
window: "60s" // per 60 seconds
});
export async function GET(req: Request) {
const ip = req.headers.get("x-forwarded-for") ?? "anonymous";
const { success } = await limiter.limit(ip);
if (!success) {
return new Response("Too Many Requests", { status: 429 });
}
// Handle request
return Response.json({ results: [] });
}That's it! You now have Redis caching and rate limiting with edge compatibility.
Stale-while-revalidate
Serve stale data instantly while fetching fresh data in background:
import { cacheSWR } from "@repo/db-upstash-redis/cache";
const data = await cacheSWR("key", fetchData, {
ttl: 60, // Fresh for 60s
staleFor: 300 // Serve stale for 5min while revalidating
});Technical Details
For Developers: Technical implementation details
Upstash Redis integration for caching, session storage, rate limiting, and real-time features. Edge-compatible with REST API.
Installation
pnpm add @repo/db-upstash-redisOverview
| Property | Value |
|---|---|
| Location | packages/db-upstash-redis |
| Dependencies | @upstash/redis, @upstash/ratelimit |
| Edge Compatible | Yes |
Export Paths
| Path | Description |
|---|---|
@repo/db-upstash-redis | Redis client instance |
@repo/db-upstash-redis/cache | Caching utilities |
@repo/db-upstash-redis/ratelimit | Rate limiting |
Basic Usage
Redis Client
import { redis } from "@repo/db-upstash-redis";
// String operations
await redis.set("key", "value");
// highlight-next-line
await redis.set("key", "value", { ex: 3600 }); // Expires in 1 hour
const value = await redis.get("key");
// Delete
await redis.del("key");
// Check existence
const exists = await redis.exists("key");Hash Operations
Use Case
Hashes are ideal for storing objects like user profiles where you need to access individual fields.
import { redis } from "@repo/db-upstash-redis";
// Set hash field
// highlight-next-line
await redis.hset("user:123", { name: "John", email: "john@example.com" });
// Get hash field
const name = await redis.hget("user:123", "name");
// Get all fields
const user = await redis.hgetall("user:123");
// Delete field
await redis.hdel("user:123", "email");List Operations
import { redis } from "@repo/db-upstash-redis";
// Push to list
await redis.lpush("queue", "item1", "item2");
await redis.rpush("queue", "item3");
// Pop from list
const item = await redis.lpop("queue");
// Get range
// highlight-next-line
const items = await redis.lrange("queue", 0, -1);Set Operations
import { redis } from "@repo/db-upstash-redis";
// Add to set
await redis.sadd("tags", "javascript", "typescript", "react");
// Check membership
const isMember = await redis.sismember("tags", "javascript");
// Get all members
const tags = await redis.smembers("tags");
// Remove from set
await redis.srem("tags", "react");Caching
Cache Wrapper
Automatic Caching
The cache wrapper automatically handles cache lookup, miss, and storage. Use it to wrap expensive database queries.
import { cache } from "@repo/db-upstash-redis/cache";
// Cache function result
// highlight-start
const getUser = cache(
async (userId: string) => {
return await prisma.user.findUnique({ where: { id: userId } });
},
{
key: (userId) => `user:${userId}`,
ttl: 3600 // 1 hour
}
);
// highlight-end
const user = await getUser("user_123");Manual Caching
import { cacheGet, cacheSet, cacheDelete } from "@repo/db-upstash-redis/cache";
// Get from cache
const cached = await cacheGet<User>("user:123");
if (!cached) {
const user = await fetchUser("123");
// highlight-next-line
await cacheSet("user:123", user, { ttl: 3600 });
}
// Invalidate cache
await cacheDelete("user:123");
// Pattern delete
await cacheDelete("user:*");Stale-While-Revalidate
SWR Pattern
SWR serves stale data immediately while fetching fresh data in the background. This provides fast responses with eventual consistency.
import { cacheSWR } from "@repo/db-upstash-redis/cache";
const data = await cacheSWR("api:data", async () => await fetchExpensiveData(), {
// highlight-start
ttl: 60, // Fresh for 60 seconds
staleFor: 300 // Serve stale for 5 minutes while revalidating
// highlight-end
});Rate Limiting
Basic Rate Limiter
import { ratelimit } from "@repo/db-upstash-redis/ratelimit";
const limiter = ratelimit({
// highlight-start
requests: 10, // 10 requests
window: "60s" // per 60 seconds
// highlight-end
});
export async function POST(req: Request) {
const ip = req.headers.get("x-forwarded-for") ?? "anonymous";
const { success, remaining, reset } = await limiter.limit(ip);
// highlight-start
if (!success) {
return new Response("Too Many Requests", {
status: 429,
headers: {
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString()
}
});
}
// highlight-end
// Handle request
}Sliding Window
Accuracy
Sliding window is more accurate than fixed window for rate limiting as it prevents burst requests at window boundaries.
import { ratelimit } from "@repo/db-upstash-redis/ratelimit";
const limiter = ratelimit({
requests: 100,
window: "1h",
// highlight-next-line
algorithm: "sliding" // More accurate than fixed window
});Token Bucket
import { ratelimit } from "@repo/db-upstash-redis/ratelimit";
const limiter = ratelimit({
requests: 10,
window: "10s",
// highlight-start
algorithm: "token-bucket",
refillRate: 1 // 1 token per second
// highlight-end
});Session Storage
import { sessionStore } from "@repo/db-upstash-redis";
// Create session
// highlight-start
const sessionId = await sessionStore.create({
userId: "user_123",
data: { role: "admin" },
ttl: 86400 // 24 hours
});
// highlight-end
// Get session
const session = await sessionStore.get(sessionId);
// Update session
await sessionStore.update(sessionId, {
lastActivity: new Date().toISOString()
});
// Delete session
await sessionStore.delete(sessionId);Pub/Sub
REST API Limitation
Upstash REST API doesn't support persistent subscriptions. For real-time features, consider using a WebSocket service or polling.
import { redis } from "@repo/db-upstash-redis";
// Publish
await redis.publish(
"notifications",
JSON.stringify({
type: "new_message",
userId: "user_123"
})
);
// Note: For subscriptions, use a long-running process
// Upstash REST API doesn't support persistent subscriptionsAtomic Operations
import { redis } from "@repo/db-upstash-redis";
// Increment
// highlight-start
const count = await redis.incr("page:views");
const byAmount = await redis.incrby("page:views", 5);
// highlight-end
// Decrement
await redis.decr("stock:item_123");
// Set if not exists
const wasSet = await redis.setnx("lock:resource", "locked");
// Expire
await redis.expire("temp:data", 3600);Pipeline
Performance
Use pipelines to batch multiple commands in a single network round-trip. This significantly improves performance for bulk operations.
import { redis } from "@repo/db-upstash-redis";
// highlight-start
const pipeline = redis.pipeline();
pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.get("key1");
pipeline.get("key2");
const results = await pipeline.exec();
// highlight-end
// [null, null, "value1", "value2"]Environment Variables
# Upstash Redis
UPSTASH_REDIS_REST_URL="https://xxx.upstash.io"
UPSTASH_REDIS_REST_TOKEN="AXxx..."Related Packages
- @repo/db-prisma - Primary database
- @repo/db-upstash-vector - Vector database
- @repo/security - Rate limiting helpers