Implement high-performance caching with Upstash Redis in Google Antigravity applications including cache invalidation and session management.
# Upstash Redis Caching Strategies
Implement high-performance caching patterns with Upstash Redis in your Google Antigravity applications. This guide covers caching strategies, session management, cache invalidation, and real-world patterns.
## Redis Client Setup
Configure a type-safe Redis client with connection pooling:
```typescript
// lib/redis.ts
import { Redis } from "@upstash/redis";
import { Ratelimit } from "@upstash/ratelimit";
// Singleton Redis client
let redis: Redis | null = null;
export function getRedis(): Redis {
if (!redis) {
redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
}
return redis;
}
// Cache wrapper with automatic serialization
export class CacheClient {
private redis: Redis;
private defaultTTL: number;
constructor(ttlSeconds = 3600) {
this.redis = getRedis();
this.defaultTTL = ttlSeconds;
}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get<T>(key);
return data;
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
await this.redis.set(key, value, {
ex: ttl || this.defaultTTL,
});
}
async delete(key: string): Promise<void> {
await this.redis.del(key);
}
async deletePattern(pattern: string): Promise<void> {
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
async getOrSet<T>(
key: string,
fetcher: () => Promise<T>,
ttl?: number
): Promise<T> {
const cached = await this.get<T>(key);
if (cached !== null) {
return cached;
}
const fresh = await fetcher();
await this.set(key, fresh, ttl);
return fresh;
}
}
export const cache = new CacheClient();
```
## Cache-Aside Pattern
Implement the cache-aside pattern for database queries:
```typescript
// lib/cached-queries.ts
import { cache } from "./redis";
import { db } from "./db";
export interface CacheOptions {
ttl?: number;
tags?: string[];
}
export async function getCachedPrompts(
category?: string,
options: CacheOptions = {}
) {
const cacheKey = category
? `prompts:category:${category}`
: "prompts:all";
return cache.getOrSet(
cacheKey,
async () => {
const prompts = await db
.selectFrom("prompts")
.selectAll()
.where("is_approved", "=", true)
.$if(!!category, (qb) =>
qb.where("tags", "@>", [category!])
)
.orderBy("created_at", "desc")
.execute();
return prompts;
},
options.ttl || 300 // 5 minutes default
);
}
export async function getCachedPromptById(id: string) {
const cacheKey = `prompt:${id}`;
return cache.getOrSet(
cacheKey,
async () => {
const prompt = await db
.selectFrom("prompts")
.selectAll()
.where("id", "=", id)
.executeTakeFirst();
return prompt || null;
},
600 // 10 minutes for individual prompts
);
}
```
## Cache Invalidation
Implement smart cache invalidation strategies:
```typescript
// lib/cache-invalidation.ts
import { cache } from "./redis";
export class CacheInvalidator {
// Invalidate specific prompt cache
async invalidatePrompt(promptId: string): Promise<void> {
await Promise.all([
cache.delete(`prompt:${promptId}`),
cache.deletePattern("prompts:*"), // Clear all list caches
]);
}
// Invalidate by category
async invalidateCategory(category: string): Promise<void> {
await cache.deletePattern(`prompts:category:${category}*`);
}
// Invalidate user-specific caches
async invalidateUserCache(userId: string): Promise<void> {
await cache.deletePattern(`user:${userId}:*`);
}
// Tag-based invalidation
async invalidateByTags(tags: string[]): Promise<void> {
const invalidations = tags.map((tag) =>
cache.deletePattern(`*:tag:${tag}:*`)
);
await Promise.all(invalidations);
}
}
export const invalidator = new CacheInvalidator();
```
## Session Management
Implement Redis-based session management:
```typescript
// lib/session.ts
import { getRedis } from "./redis";
import { nanoid } from "nanoid";
interface Session {
userId: string;
email: string;
createdAt: number;
expiresAt: number;
metadata?: Record<string, unknown>;
}
export class SessionManager {
private redis = getRedis();
private readonly SESSION_PREFIX = "session:";
private readonly SESSION_TTL = 7 * 24 * 60 * 60; // 7 days
async createSession(userId: string, email: string): Promise<string> {
const sessionId = nanoid(32);
const session: Session = {
userId,
email,
createdAt: Date.now(),
expiresAt: Date.now() + this.SESSION_TTL * 1000,
};
await this.redis.set(
`${this.SESSION_PREFIX}${sessionId}`,
session,
{ ex: this.SESSION_TTL }
);
// Store user's active sessions for management
await this.redis.sadd(`user:${userId}:sessions`, sessionId);
return sessionId;
}
async getSession(sessionId: string): Promise<Session | null> {
return this.redis.get<Session>(`${this.SESSION_PREFIX}${sessionId}`);
}
async destroySession(sessionId: string): Promise<void> {
const session = await this.getSession(sessionId);
if (session) {
await this.redis.srem(`user:${session.userId}:sessions`, sessionId);
}
await this.redis.del(`${this.SESSION_PREFIX}${sessionId}`);
}
async destroyAllUserSessions(userId: string): Promise<void> {
const sessionIds = await this.redis.smembers(`user:${userId}:sessions`);
if (sessionIds.length > 0) {
const keys = sessionIds.map((id) => `${this.SESSION_PREFIX}${id}`);
await this.redis.del(...keys);
}
await this.redis.del(`user:${userId}:sessions`);
}
}
export const sessionManager = new SessionManager();
```
## Best Practices
1. **TTL Strategy**: Set appropriate TTLs based on data volatility
2. **Key Namespacing**: Use consistent key prefixes for organization
3. **Serialization**: Use JSON for complex objects, avoid storing large blobs
4. **Connection Pooling**: Reuse connections across requests
5. **Error Handling**: Implement fallbacks when cache is unavailable
6. **Monitoring**: Track cache hit rates and memory usageThis redis prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their redis implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For redis projects, consider mentioning your framework version, coding style, and any specific libraries you're using.