Caching Strategies
Implement effective caching strategies for databases, APIs, and frontends using Redis, CDNs, and browser caches.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
Caching is the single most effective technique for improving application performance. By storing frequently accessed data closer to consumers — in browser memory, CDN edges, or in-memory stores — you reduce latency, decrease database load, and improve user experience. Choosing the right caching strategy depends on data freshness requirements and read/write patterns.
When to Use
Use this resource when:
- Database queries are becoming a bottleneck under load
- API response times exceed 200ms for read-heavy endpoints
- Serving static assets (images, JS, CSS) to global users via CDN
- Building high-traffic applications where stale data is acceptable
Solution
Redis Cache-Aside (Node.js)
const redis = require('redis');
const client = redis.createClient();
async function getUser(userId) {
const cacheKey = `user:${userId}`;
// Try cache first
const cached = await client.get(cacheKey);
if (cached) return JSON.parse(cached);
// Cache miss: query database
const user = await db.users.findById(userId);
if (user) {
await client.setEx(cacheKey, 3600, JSON.stringify(user)); // TTL 1 hour
}
return user;
}
Stale-While-Revalidate (HTTP)
// Express middleware
app.get('/api/products', (req, res) => {
res.set('Cache-Control', 'public, max-age=60, stale-while-revalidate=300');
// Clients can use cached data for 5 minutes while revalidating in background
res.json(products);
});
CDN Edge Caching (CloudFront/Vercel)
{
"routes": [
{
"src": "/api/public/.*",
"headers": {
"Cache-Control": "public, s-maxage=86400, stale-while-revalidate=86400"
}
}
]
}
Explanation
| Strategy | Pattern | Best For |
|---|---|---|
| Cache-Aside | Application checks cache, falls back to DB | Read-heavy; simple to implement |
| Read-Through | Cache proxies DB transparently | Read-heavy; library handles logic |
| Write-Through | Writes update cache and DB simultaneously | Data consistency critical |
| Write-Behind | Writes update cache; async DB flush | Write-heavy; eventual consistency |
| Refresh-Ahead | Background refresh before expiry | Predictable access patterns |
Cache invalidation approaches:
- Time-based (TTL): Simple but can serve stale data
- Key-based: Include version or hash in cache key
- Event-based: Invalidate on data change via message bus. See cache invalidation.
Variants
| Layer | Technology | Latency | Use Case |
|---|---|---|---|
| Browser | LocalStorage, IndexedDB | ~1ms | Offline-first apps |
| CDN | CloudFront, Cloudflare, Fastly | ~10-50ms | Static assets, API edge caching |
| Application | Redis, Memcached | ~1ms | Session store, hot data |
| Database | Query cache, materialized views | ~1-10ms | Repeated complex queries |
| Disk | Page cache, OS buffers | ~0.1ms | File system reads |
Best Practices
- Set TTLs based on data volatility: User profiles (1h), product catalogs (24h), stock prices (10s)
- Cache at multiple layers: Browser + CDN + Redis + DB query cache
- Use cache stampsede protection: Lock during cache miss to prevent thundering herd
- Monitor hit rates: Below 80% signals misconfiguration or too-short TTL
- Version your cache keys: Include app version to invalidate on deploy
Common Mistakes
- Caching everything: Static data yes; user-specific or rapidly changing data no
- No invalidation strategy: Stale data persists indefinitely without TTL or events
- Thundering herd: 1000 requests hit a cold cache simultaneously; use locking
- Cache poisoning: Unvalidated user input stored in shared cache affects all users
- Ignoring cache warming: Production deploys start with empty caches and high latency
Frequently Asked Questions
Q: How do I prevent cache stampedes? A: Use a mutex or Redis SET NX (lock) so only one request rebuilds the cache while others wait.
Q: Should I cache GraphQL responses? A: Yes, but cache by query hash + variables. Apollo Server has built-in response caching.
Q: What is the difference between Redis and Memcached? A: Redis supports data structures (lists, sets, sorted sets) and persistence. Memcached is simpler and slightly faster for plain key-value caching.
Related Resources
Web Performance Optimization Guide
A comprehensive guide to optimizing web application performance for better Core Web Vitals and user experience.
PatternProxy Pattern for API Response Caching
How to implement a caching proxy that intercepts API calls and stores responses to reduce latency and avoid redundant network requests
RecipeRedis Cache Patterns for High-Performance Applications
How to implement cache-aside, write-through, and write-behind patterns with Redis to reduce database load and improve response times
RecipeImplement Cache Invalidation Strategies
How to keep caches consistent with databases using TTL, write-through, write-behind, and event-driven invalidation patterns.
RecipeImplement CDN Edge Caching
Configure content delivery networks with edge caching rules, cache invalidation, and geographic optimization for static and dynamic content.