Redis Caching: Speed Up Your Next.js App
16 May 2026 · by Yunmin Shin
Why Add Redis to a Next.js Application?
Next.js has built-in caching for fetch requests and page routes. But there are scenarios where you need a shared, persistent cache that exists outside the Next.js request lifecycle:
- Caching expensive database queries that multiple users trigger simultaneously
- Storing session data for authenticated users
- Rate limiting API endpoints across serverless function instances
- Debouncing expensive operations like sending emails or generating reports
Redis is the standard solution for all of these. It is an in-memory key-value store with sub-millisecond response times and rich data structures (strings, hashes, lists, sets, sorted sets).
Which Redis Service Should You Use?
Upstash is the recommended Redis service for Next.js and serverless applications. It is HTTP-based rather than TCP-based, which means it works correctly in serverless environments (Vercel, Cloudflare Workers) where persistent TCP connections are not supported. Upstash has a generous free tier and a Singapore region for low-latency access from Thailand.
npm install @upstash/redis
Configure via environment variables:
UPSTASH_REDIS_REST_URL=https://...
UPSTASH_REDIS_REST_TOKEN=...
What Is the Cache-Aside Pattern?
Cache-aside is the most common caching pattern. The application checks the cache first; if the data is there (cache hit), return it immediately. If not (cache miss), fetch from the database, store in cache, and return:
async function getProductById(id: string) {
const cached = await redis.get(`product:${id}`);
if (cached) return cached as Product;
const product = await db.query.products.findFirst({ where: eq(products.id, id) });
await redis.set(`product:${id}`, product, { ex: 3600 }); // Cache for 1 hour
return product;
}
The ex: 3600 sets an expiry of 3600 seconds (1 hour). Expired keys are automatically deleted by Redis.
How Do You Choose TTL Values?
Time-to-live (TTL) defines how long a cached value remains valid before it must be refreshed:
- User sessions: 24 hours to 7 days depending on your security requirements
- Product catalog data: 1–4 hours (changes infrequently, slight staleness acceptable)
- Inventory/stock levels: 60–300 seconds (changes frequently, staleness costly)
- Exchange rates or pricing: 30–300 seconds
- Rate limit counters: 1 minute to 1 hour depending on the limit window
When in doubt, start with a shorter TTL and increase it as you observe cache hit rates and data freshness requirements in production.
How Do You Invalidate Cache on Data Change?
When a product is updated in the database, the cached version is stale. Delete it immediately:
await redis.del(`product:${product.id}`);
For more complex invalidation — when updating a category should invalidate all products in that category — use cache tags: store a list of cache keys under a tag key, then delete all keys in the list when the tag is invalidated.
How Do You Use Redis for Rate Limiting?
Upstash provides a @upstash/ratelimit package that implements rate limiting algorithms on top of Redis:
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, "1 m"), // 10 requests per minute
});
const { success } = await ratelimit.limit(identifier);
if (!success) return Response.json({ error: "Too many requests" }, { status: 429 });
This is essential for any public-facing Next.js API route or AI-powered endpoint where cost or abuse is a concern.
Ready to Build Something Fast?
Get a free quote on LINE. We reply within 24 hours.
Ready to build something fast and scalable?
Get a free project quote on LINE. We reply within 24 hours.
무료 견적 on LINE