Node.js cookbook · Beginner
Cache expensive lookups in Redis from Node.js
Reduce database load for a hot endpoint while avoiding stampedes when entries expire.
Approach
Implement cache-aside with JSON values and TTL jitter. Deduplicate concurrent misses inside a process with an in-flight Map of promises, and optionally across processes with a short Redis lock.
Solution
import { createClient } from "redis";
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();
const inflight = new Map<string, Promise<unknown>>();
export async function cached<T>(key: string, ttlSeconds: number, load: () => Promise<T>): Promise<T> {
const hit = await redis.get(key);
if (hit !== null) return JSON.parse(hit) as T;
const pending = inflight.get(key) as Promise<T> | undefined;
if (pending) return pending; // coalesce concurrent misses in this process
const promise = (async () => {
try {
const value = await load();
const jitter = Math.floor(Math.random() * ttlSeconds * 0.1);
await redis.set(key, JSON.stringify(value), { EX: ttlSeconds + jitter });
return value;
} finally {
inflight.delete(key);
}
})();
inflight.set(key, promise);
return promise;
}
app.get("/api/products/:id", async (req, res) => {
const product = await cached(`product:v1:${req.params.id}`, 300, () => products.get(req.params.id));
if (!product) return res.status(404).json({ title: "Not Found" });
res.json(product);
});
export const invalidateProduct = (id: string) => redis.del(`product:v1:${id}`);
Pitfalls
- Caching per-user responses under shared keys.
- Not versioning keys when the value shape changes.
- Treating the cache as the source of truth.
Variations
- In-process LRU (
lru-cache) in front of Redis for ultra-hot keys. - HTTP caching headers + CDN for public data.
Further reading: Redis: Node.js client
Reference: https://redis.io/docs/latest/develop/clients/nodejs/
More Node.js recipes
- Add health and readiness endpoints to a Node.js service
- Add JWT authentication to an Express API
- Call external APIs with timeouts and retries in Node.js
- Centralize error handling in Express 5
- Hash passwords and implement login safely in Node.js
- Implement cursor pagination with PostgreSQL in Node.js
- Offload CPU-heavy work with worker threads
- Process background jobs with BullMQ