Caching & CDN Strategy Planner
Design effective caching at all layers.
Caching Layers
Client → CDN (Edge) → Server Cache → Database
CDN Configuration (CloudFront)
typescript1const distribution = { 2 Origins: [ 3 { 4 DomainName: "api.example.com", 5 CustomHeaders: [ 6 { 7 HeaderName: "X-CDN-Secret", 8 HeaderValue: process.env.CDN_SECRET, 9 }, 10 ], 11 }, 12 ], 13 DefaultCacheBehavior: { 14 ViewerProtocolPolicy: "redirect-to-https", 15 AllowedMethods: ["GET", "HEAD", "OPTIONS"], 16 CachedMethods: ["GET", "HEAD"], 17 Compress: true, 18 DefaultTTL: 86400, // 1 day 19 MaxTTL: 31536000, // 1 year 20 MinTTL: 0, 21 ForwardedValues: { 22 QueryString: true, 23 Cookies: { Forward: "none" }, 24 Headers: ["Accept", "Accept-Encoding"], 25 }, 26 }, 27 CacheBehaviors: [ 28 { 29 PathPattern: "/api/static/*", 30 DefaultTTL: 31536000, // 1 year - never changes 31 }, 32 { 33 PathPattern: "/api/dynamic/*", 34 DefaultTTL: 300, // 5 min - changes frequently 35 }, 36 ], 37};
Server-side Caching (Redis)
typescript1import Redis from 'ioredis'; 2 3const redis = new Redis(process.env.REDIS_URL); 4 5async function getCachedOrFetch<T>( 6 key: string, 7 fetcher: () => Promise<T>, 8 ttl: number = 3600 9): Promise<T> { 10 // Try cache 11 const cached = await redis.get(key); 12 if (cached) { 13 return JSON.parse(cached); 14 } 15 16 // Fetch and cache 17 const data = await fetcher(); 18 await redis.setex(key, ttl, JSON.stringify(data)); 19 20 return data; 21} 22 23// Usage 24app.get('/api/user/:id', async (req, res) => { 25 const user = await getCachedOrFetch( 26 \`user:\${req.params.id}\`, 27 () => prisma.user.findUnique({ where: { id: req.params.id } }), 28 3600 29 ); 30 31 res.json(user); 32});
Cache Invalidation
typescript1// Invalidate on update 2app.put('/api/user/:id', async (req, res) => { 3 const user = await prisma.user.update({ 4 where: { id: req.params.id }, 5 data: req.body, 6 }); 7 8 // Invalidate cache 9 await redis.del(\`user:\${req.params.id}\`); 10 11 // Invalidate CDN 12 await cloudfront.createInvalidation({ 13 DistributionId: DISTRIBUTION_ID, 14 InvalidationBatch: { 15 Paths: { Items: [\`/api/user/\${req.params.id}\`] }, 16 CallerReference: Date.now().toString(), 17 }, 18 }); 19 20 res.json(user); 21});
Cache Headers
typescript1app.get("/api/products", (req, res) => { 2 res.set({ 3 "Cache-Control": "public, max-age=3600", // Browser + CDN: 1h 4 ETag: generateETag(products), 5 "Last-Modified": new Date(products.updatedAt).toUTCString(), 6 }); 7 8 res.json(products); 9}); 10 11app.get("/api/user/profile", (req, res) => { 12 res.set({ 13 "Cache-Control": "private, no-cache", // No caching (sensitive) 14 }); 15 16 res.json(profile); 17});
Output Checklist
- CDN configured
- Server cache implemented
- Invalidation strategy
- Cache headers set
- Monitoring configured ENDFILE