>_
EngineeringNotes
Back to Next.js Mastery

Data Fetching & Caching

Next.js unifies data fetching and caching at the framework level, optimized for performance and developer experience in the App Router.

1. Big Picture

In modern Next.js (App Router), data fetching and caching are built directly into the framework, eliminating manual implementation common in traditional React apps.

Why Caching Matters (System Design Angle)

Without Caching

Every request → DB/API call → Slow + Expensive. Backend becomes a bottleneck as users scale.

With Caching

First request → Fetch → Cache.
Next requests → Serve from cache → Fast ⚡.

2. Types of Caching in Next.js

Next.js utilizes multiple specialized layers to maximize performance and minimize infrastructure costs:

🔵 1. Request Memoization (React Level)

What it is

Same request in a single render tree is executed only once.

Example

await fetch("/api/data");
await fetch("/api/data"); // reused, not called twice

Flow

Render starts
Fetch called
Stored in memory
Next same fetch → reused

Benefit

✔ Avoid duplicate API calls | ✔ Faster rendering

2. Data Cache (Next.js Level)

What it is

Persistent cache across requests (server-side).

Example

fetch(url, { cache: "force-cache" });

Flow

Request 1 → Fetch
Cache stored
Request 2 → Served from cache

Benefit

✔ Reduces backend load | ✔ Improves response time

3. Full Route Cache (HTML Cache)

What it is

Entire page HTML is cached for high-speed delivery.

Used in: SSG, ISR

Flow

First request
Render page
Cache HTML
Next request → Serve HTML directly

Benefit

✔ Instant load time | ✔ Zero server work per request

4. CDN / Edge Cache

What it is

Cache stored geographically closer to users.

Flow

User (India)
NEAREST CDN NODE
RESPONSE

Benefit

✔ Ultra-low latency | ✔ Global scalability

3. Data Fetching in App Router

Key Change (VERY IMPORTANT)

Next.js has deprecated getServerSideProps and getStaticProps. Data is now fetched directly inside components using async/await.

  • Cleaner, more intuitive code architecture
  • Framework-level caching handles the complexity
  • Directly accessible from Server Components
page.js
javascript
async function Page() {
  const res = await fetch("https://api.com/data");
  const data = await res.json();

  return <div>{data.name}</div>;
}

4. Fetch Options (VERY IMPORTANT)

🔵 1. Default Behavior

Automatic caching (SSG-like). Same data served consistently.

fetch(url)

🟢 2. Force Cache

Strict static serving. Ensures data remains cached.

{ cache: "force-cache" }

🔴 3. No Store

Always fresh data (SSR-like). Bypasses cache entirely.

{ cache: "no-store" }

🟡 4. Revalidation

Time-based update (ISR). Refresh every N seconds.

{ next: { revalidate: 60 } }
Fetch StrategyCache ResultTypical Use Case
force-cacheyesStatic documentation, blog posts
no-storeNoReal-time stock prices, banking dashboards
revalidatePartialE-commerce product listings, news feeds

5. ISR (Incremental Static Regeneration)

Definition

Update cached data after deployment without rebuilding the entire site. Combines speed with freshness.

Lifecycle Flow

USER REQUEST → SERVE CACHED PAGE
BACKGROUND REVALIDATION STARTS
CACHE UPDATED FOR NEXT USER

Core Benefit

"Combines the static speed of SSG with the data freshness of SSR."

6. Revalidation Types

🟢 1. Time-based

Auto-refresh after a specific time window.

revalidate: 60

🔵 2. On-demand (Advanced)

Invalided cache manually via triggers (e.g. Webhooks).

revalidateTag("products");

7. Cache Invalidation

❗ The Problem: Stale Data

Caches are great until the underlying data changes. Delivering outdated information to users is a critical system failure.

Modern Solutions

1. Time-based Expiry

Low precision. Good for high-traffic public feeds.

2. Tag-based Invalidation

High precision. Pure system design gold for e-commerce and dynamic apps.

Tag Invalidation Flow

UPDATE PRODUCT
INVALIDATE "PRODUCTS" TAG
NEXT REQUEST → FRESH DATA
fetch-and-revalidate.js
javascript
// Fetching with a tag
fetch(url, {
  next: { tags: ["products"] }
});

// Implementation: Action Invalidation
import { revalidateTag } from 'next/cache';

async function updateProduct() {
  await db.update();
  // Clear all cached responses with this tag
  revalidateTag("products"); 
}

8. Streaming + Data Fetching

Problem (Without Streaming)

The server must wait for all data to be fetched before it can send the final HTML. Users see a blank screen for seconds.

Solution

Render and send UI in chunks as data arrives. Header and layout arrive instantly; content follows.

Progressive Example

NAVBAR INSTANT ⚡
PRODUCTS LOADS LATER...
REVIEWS LOADS LATER...

Using React Suspense for Streaming

dashboard/page.js
javascript
import { Suspense } from 'react';
import { ProductList, Skeleton } from './components';

export default function Page() {
  return (
    <section>
      <h1>New Arrivals</h1>
      {/* Component rendered on server, streamed through fallback */}
      <Suspense fallback={<Skeleton />}>
        <ProductList />
      </Suspense>
    </section>
  );
}

9. Parallel vs Sequential Fetching

❌ Sequential (Slow)
javascript
javascript
// Waterfall: B waits for A to finish
const user = await fetch(urlA);
const posts = await fetch(urlB);

Total time: A + B

Parallel (Fast)
javascript
javascript
// Simultaneous Execution
const [user, posts] = await Promise.all([
  fetch(urlA),
  fetch(urlB)
]);

Total time: Max(A, B)

10. Real-World Architecture

E-commerce Design Strategy

PRODUCT PAGE

DATA

Info

SSG / ISR

DATA

Reviews

ISR (60s)

DATA

Cart Info

no-store (SSR)

Insight: Different parts of the same page can use completely different caching strategies.

11. Common Mistakes

Universal 'no-store'

Using no-store everywhere kills the edge-caching benefits and spikes server load.

Over-caching

Caching real-time data like stock prices for too long leads to stale, invalid UI.

Missing Invalidation

Forgetting to revalidate after updates causes users to see outdated info indefinitely.

12. Performance Strategy (Interview GOLD)

🧠 The Next.js Best Practice Matrix

Static Content

force-cache

Semi-Dynamic

revalidate

Strict Dynamic

no-store

13. Advanced Interview Questions

Next.js provides built-in caching and data-fetching mechanisms that allow developers to control performance and freshness.

Scalability + Speed + Freshness