Learning Next.js - Data Fetching & API Routes
Episode 6 of 24

Learning Next.js - Data Fetching & API Routes

This episode dissects data fetching in the App Router: server-side fetch with async server components, static generation using generateStaticParams, Incremental Static Regeneration with revalidate, and route handlers for building a backend API.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

A real application needs data. The key question isn't only how to fetch data, but when and where — on the server at build time, on the server per request, or on the client after the page loads. Episode 6 answers that question.

We'll cover server-side data fetching in the App Router, static generation with generateStaticParams, Incremental Static Regeneration (ISR) with revalidate, and route handlers for building a simple backend API.

Server-Side Data Fetching in the App Router

Async Server Components

In the App Router, a server component can be async and use fetch directly:

Fetch data in a server component
export default async function Posts() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts")
  const posts = await res.json()
 
  return (
    <ul>
      {posts.slice(0, 5).map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

The direct fetch inside the server component above runs on the server, without loading the client down. The key in list rendering from episode 0 — key={post.id} — avoids React warnings when a list is rendered.

Caching and Revalidation on fetch

Next.js's built-in fetch function supports caching and time-based revalidation:

Fetch with revalidation
const res = await fetch("https://api.example.com/products", {
  next: { revalidate: 3600 },
})

The next: { revalidate: 3600 } option caches the data and refreshes it in the background every hour. This is the basic mechanism of Incremental Static Regeneration.

Static Site Generation

generateStaticParams and Dynamic Segments

For dynamic pages whose data rarely changes, combine generateStaticParams with static mode. All pages are built during npm run build:

SSG for dynamic pages
export async function generateStaticParams() {
  const res = await fetch("https://api.example.com/articles")
  const articles = await res.json()
 
  return articles.map((a) => ({ slug: a.slug }))
}
 
export default async function Article({ params }) {
  const { slug } = await params
  const res = await fetch(`https://api.example.com/articles/${slug}`)
  const article = await res.json()
 
  return <h1>{article.title}</h1>
}

All article slugs are registered in generateStaticParams and prerendered at build time. Pages that aren't registered automatically return a 404 — unless the dynamicParams option is enabled.

Incremental Static Regeneration and Revalidation

Time-Based and On-Demand Revalidation

ISR keeps static pages up to date. There are two ways to trigger revalidation: time-based with next: { revalidate: 60 }, or on-demand by calling an API:

On-demand revalidation in a route handler
import { revalidatePath } from "next/cache"
 
export async function POST(request) {
  await request.json()
  revalidatePath("/articles")
  return new Response("Cache dibersihkan", { status: 200 })
}

revalidatePath("/articles") forces the /articles page to be rebuilt when the endpoint above is called. This pattern is useful after data in a CMS is updated, without waiting for a time window.

API Routes and Route Handlers

Building a Backend with route.ts

The App Router builds APIs through a route.ts file that exports handlers based on HTTP methods. Create app/api/items/route.ts:

GET and POST route handlers
import { NextResponse } from "next/server"
 
export async function GET() {
  return NextResponse.json({ items: ["apel", "mangga"] })
}
 
export async function POST(request) {
  const body = await request.json()
  return NextResponse.json({ created: body }, { status: 201 })
}

The endpoint above is available at /api/items. NextResponse.json simplifies JSON responses, and the 201 status indicates the resource was created successfully. These route handlers are the foundation of a Next.js backend API that we'll secure in episode 13.

Closing

Here's what to take away:

  • Async server components can use fetch directly for data.
  • The revalidate option enables time-based caching.
  • generateStaticParams builds static pages at build time.
  • ISR combines static speed with data freshness.
  • revalidatePath triggers on-demand cache updates.
  • Route handlers in app/api/route.ts build the backend API.

In the next episode, episode 7, we'll discuss state management and hooks — local state with React hooks, the use client directive and server-client boundaries, shared state with Context API, Zustand, and TanStack Query, as well as the client component versus server component lifecycle. Your application's interactivity will start to take shape.

Learning Next.js - Data Fetching & API Routes | Learn Next.js