Cache database query results, set TTL expiration, implement cache invalidation, and measure hit rates.
Also called lazy loading. Check the cache first. If found (cache hit), return it. If not (cache miss), fetch from the database, store in cache, return to caller.
import { createClient } from 'redis';
const redis = createClient();
await redis.connect();
async function getUser(id) {
const cacheKey = `user:${id}`;
// 1. Check cache
const cached = await redis.get(cacheKey);
if (cached) {
console.log('Cache HIT');
return JSON.parse(cached);
}
// 2. Cache miss → query database
console.log('Cache MISS');
const user = await db.query('SELECT * FROM users WHERE id = $1', [id]);
// 3. Store in cache with TTL
await redis.setEx(cacheKey, 3600, JSON.stringify(user));
return user;
}
// Invalidate cache when user is updated
async function updateUser(id, data) {
await db.query('UPDATE users SET ...');
await redis.del(`user:${id}`); // invalidate
}// Write to cache AND database together
async function createPost(data) {
const post = await db.create(data);
const cacheKey = `post:${post.id}`;
await redis.setEx(cacheKey, 1800, JSON.stringify(post));
// Also invalidate the list cache
await redis.del('posts:all');
return post;
}resource:id or resource:filter:value. Example: user:123, posts:page:2, products:category:electronics. This makes bulk invalidation possible with SCAN.user:123, posts:page:2. Consistent naming enables bulk operations.