JSTAcademy
0 XP
Dashboard
Crash Courses
Data Fetching & Caching
11 min
Basic+150 XP
Crash Courses · Basic

Data Fetching & Caching

Fetch data with server components, React cache, and Next.js fetch extensions.
11 min read+150 XP on completionCert: Next.js Crash Course
Tap any word in the text below to start reading from there.

Data Fetching & Caching

In the App Router, data fetching happens directly in server components — no useEffect, no loading state, no separate API call. The component async/awaits the data and renders it server-side.

Next.js extends the native fetch API with caching options, and provides React.cache() for per-request deduplication.

The Three Fetching Strategies

1. Static (SSG) — build once, serve forever

// app/courses/page.tsx
// Fetches at BUILD TIME — result cached forever
const res = await fetch('https://api.example.com/courses', {
  cache: 'force-cache'
})
const courses = await res.json()

Fastest possible delivery — the HTML is pre-generated and served from CDN. Use for content that rarely changes.

2. ISR — rebuild on a schedule

// Fetch with 60-second revalidation
const res = await fetch('https://api.example.com/courses', {
  next: { revalidate: 60 }
})

// Or set revalidation at the page level
export const revalidate = 60  // all fetches in this page revalidate every 60s

export default async function CoursesPage() {
  const courses = await getCourses()
  return <CourseList courses={courses} />
}

The page is served from cache. After 60 seconds, the next request triggers a silent background regeneration. Users always get a fast response; data stays fresh.

3. Dynamic (SSR) — fresh on every request

const res = await fetch('https://api.example.com/user/profile', {
  cache: 'no-store'  // bypass cache entirely — always fresh
})

Use for user-specific data — personalized content, real-time balances, private records that must never be cached across users.

generateStaticParams — Pre-render Dynamic Routes

// app/courses/[id]/page.tsx
export async function generateStaticParams() {
  const courses = await getCourses()
  // Returns all the [id] values to pre-generate at build time
  return courses.map(c => ({ id: c.id }))
}

export default async function CoursePage({
  params
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const course = await getCourse(id)

  if (!course) return notFound()

  return <CourseDetail course={course} />
}

At build time, Next.js calls generateStaticParams, gets the list of IDs, and generates a static HTML page for each one. Visiting /courses/cc-react-m01 serves pre-built HTML instantly.

React cache() — Per-Request Deduplication

import { cache } from 'react'
import { COURSES } from '@/lib/courses'

// Wrapping getCourse in cache() deduplicates calls within one request
export const getCourse = cache(async (id: string) => {
  // In a real app, this would be a database query
  return COURSES.find(c => c.id === id) ?? null
})

// app/courses/[id]/page.tsx — calls getCourse(id) for the page
// app/courses/[id]/layout.tsx — also calls getCourse(id) for the breadcrumb
// Result: getCourse() executes once — both components share the result

Without cache(), two server components calling getCourse('cc-react-m01') in the same request would hit the database twice. With cache(), the second call returns the memoized result.

0%