Topic 4 of 47
Fetching
Overview
Next.js App Router makes data fetching intuitive — Server Components can be async and await data directly. Next.js extends the native fetch API with caching and revalidation options, replacing the old getStaticProps/getServerSideProps patterns.
Syntax
typescript
// In a Server Component — just use async/await
async function BlogPost({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`, {
next: { revalidate: 3600 }, // cache for 1 hour (ISR)
}).then(r => r.json());
return <article>{post.content}</article>;
}
// Caching options
fetch(url, { cache: 'force-cache' }); // Static — cache indefinitely
fetch(url, { cache: 'no-store' }); // Dynamic — never cache
fetch(url, { next: { revalidate: 60 } }); // ISR — revalidate every 60s
fetch(url, { next: { tags: ['products'] }}); // Tag-based revalidation
// Revalidate from a Server Action
import { revalidateTag } from 'next/cache';
revalidateTag('products'); // clears all fetches tagged 'products'Common Pitfalls
- Each fetch request in Next.js is independently cached — two components fetching the same URL get deduped automatically in one request.
- cache: 'no-store' makes the entire page dynamic (like getServerSideProps) — avoid for high-traffic pages.
- Interview tip: ISR (Incremental Static Regeneration) — pages are static but regenerate in the background after the revalidate interval. Best of both worlds.
Real-World Example
A product page with static generation and on-demand revalidation:
example
typescript
// This page is statically generated at build time
// for all known product slugs
export async function generateStaticParams() {
const products = await db.getAllProductSlugs();
return products.map(p => ({ slug: p.slug }));
}
export async function generateMetadata({ params }) {
const product = await getProduct(params.slug);
return {
title: `${product.name} | Shop DevNotes`,
description: product.description,
openGraph: { images: [product.imageUrl] },
};
}
export default async function ProductPage({ params }) {
const product = await fetch(`${API}/products/${params.slug}`, {
next: {
revalidate: 300, // Re-generate every 5 minutes
tags: [`product-${params.slug}`]
}
}).then(r => r.json());
return <ProductDisplay product={product} />;
}
// Server Action to revalidate after admin update
async function updateProduct(id, data) {
'use server';
await db.products.update(id, data);
revalidateTag(`product-${id}`); // instantly updates the page
}