Next.js On-Demand ISR Revalidation System

hardTypeScript

Lesson

On-Demand ISR and Cache Invalidation Patterns

Incremental Static Regeneration (ISR) with on-demand revalidation is a powerful pattern that combines the performance benefits of static generation with the flexibility of dynamic content updates. This approach allows you to serve pre-generated static content while providing a mechanism to update specific pages without rebuilding your entire application.

The core concept revolves around stale-while-revalidate caching strategy combined with selective cache invalidation. When a page is first requested, it's generated and cached. Subsequent requests are served from the cache instantly. However, when content needs to be updated, an authorized API call can mark specific pages for revalidation.

The system typically involves three key components: a page generator that creates and caches static content, a revalidation API that handles cache invalidation requests with proper security, and a controller that orchestrates the interaction between these components.

Security is crucial in revalidation systems. Without proper authentication, malicious actors could trigger expensive regeneration operations or clear your entire cache. Most implementations use secret keys, webhook signatures, or API tokens to ensure only authorized systems can trigger revalidation.

The beauty of on-demand ISR lies in its granular control. Unlike time-based revalidation where pages expire after a set duration, on-demand revalidation allows you to update content exactly when needed. This is particularly valuable for e-commerce sites where product information changes, blogs where posts are updated, or any application where content freshness varies by page.

Implementation-wise, the pattern requires careful coordination between caching logic and invalidation mechanisms. The cache must track not just the data, but also metadata like generation timestamps and revalidation flags to make intelligent decisions about when to serve cached content versus generating fresh content.

Example
1// Simple cache-with-revalidation pattern 2class ContentCache { 3 private cache = new Map(); 4 private revalidationFlags = new Set(); 5 6 async get(key: string) { 7 // If marked for revalidation, generate fresh content 8 if (this.revalidationFlags.has(key)) { 9 this.revalidationFlags.delete(key); 10 const fresh = await this.generate(key); 11 this.cache.set(key, fresh); 12 return { ...fresh, cached: false }; 13 } 14 15 // Otherwise serve from cache or generate if missing 16 if (this.cache.has(key)) { 17 return { ...this.cache.get(key), cached: true }; 18 } 19 20 const content = await this.generate(key); 21 this.cache.set(key, content); 22 return { ...content, cached: false }; 23 } 24 25 markForRevalidation(key: string) { 26 this.revalidationFlags.add(key); 27 } 28 29 private async generate(key: string) { 30 // Expensive content generation 31 return { data: `Content for ${key}`, timestamp: Date.now() }; 32 } 33}
L5Check revalidation flags first - this ensures fresh content after invalidation
L12Serve from cache when available and not invalidated
L20Simple flag-based invalidation - production systems might use timestamps or versions

Key Takeaways

  • •On-demand ISR provides granular cache control, allowing updates to specific pages without full rebuilds
  • •Security is essential - revalidation endpoints must be protected with secrets, tokens, or signatures
  • •The pattern separates cache management from content generation, enabling flexible invalidation strategies
Loading...