← Study Guide· Part III: Next.js Framework Mastery
9

Caching — The Most Important Topic

9. Caching — The Most Important Topic

Caching is where most customer problems originate. Understanding every cache layer is essential.

9.1 The four cache layers

Layer 1: Browser Cache        (client-side, user's browser)
Layer 2: Edge Cache           (Vercel CDN, global PoPs)
Layer 3: Data Cache           (Next.js fetch() cache, per-function)
Layer 4: Full Route Cache     (rendered HTML cache, stored on CDN)

9.2 Next.js fetch() cache

// DEFAULT: cached indefinitely (force-cache)
fetch('https://api.example.com/data')

// Cache for 60 seconds, then revalidate
fetch('https://api.example.com/data', { next: { revalidate: 60 } })

// Never cache (always fresh)
fetch('https://api.example.com/data', { cache: 'no-store' })

// Tag-based: revalidate all fetches with this tag
fetch('https://api.example.com/products', { next: { tags: ['products'] } })

9.3 On-demand revalidation

import { revalidateTag, revalidatePath } from 'next/cache';

// When a product is updated in your CMS, call this API route:
export async function POST(request) {
  const { productId } = await request.json();
  
  revalidateTag('products');                    // revalidate all tagged fetches
  revalidatePath(`/products/${productId}`);     // revalidate specific page
  
  return Response.json({ revalidated: true });
}

This is the webhook pattern — your CMS (Contentful, Sanity, etc.) triggers a webhook on publish, which calls your Next.js API route, which revalidates the relevant cached pages. This is how ISR + headless CMS works in production.

9.4 unstable_cache (for non-fetch data)

For database queries and other data sources that don't use fetch:

import { unstable_cache } from 'next/cache';
import { db } from '@/lib/database';

const getCachedProducts = unstable_cache(
  async () => {
    return db.query('SELECT * FROM products');
  },
  ['products'],           // cache key
  { 
    revalidate: 3600,     // 1 hour
    tags: ['products']    // revalidate with revalidateTag('products')
  }
);

9.5 Common caching anti-patterns (for code audits)

Anti-patternProblemFix
fetch() in a Client ComponentBypasses server-side cache, exposes API keysMove to Server Component
cache: 'no-store' on every fetchEvery request hits origin — kills performance, costs moneyAdd appropriate revalidate
No cache tagsCan't do targeted on-demand revalidationAdd tags to related fetches
ISR without webhooksContent updates not visible until revalidate period expiresAdd CMS webhook → revalidatePath
Caching user-specific dataUser A sees User B's dataAdd user ID to cache key or disable cache
Caching in Edge MiddlewareEdge middleware runs on every request — should not cacheMove caching to Serverless layer