Back to roadmaps nextjs Course

Data Fetching Patterns

Next.js simplifies data fetching by extending the native fetch Web API on the server. You can fetch database records or API payloads directly inside your component body.


1. Async Data Fetching in Server Components

Since Server Components run on the server, you can mark them as async functions and use await directly inside the body. This eliminates the need for useEffect hooks or state management variables for data loading:

// src/app/users/page.tsx
interface User {
  id: number;
  name: string;
  email: string;
}

export default async function UsersPage() {
  // Fetch data directly on the server
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  
  if (!res.ok) {
    throw new Error("Failed to load user records");
  }

  const users: User[] = await res.json();

  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold">System Users</h1>
      <ul className="mt-4 space-y-2">
        {users.map(user => (
          <li key={user.id} className="p-4 border rounded">
            <p className="font-semibold">{user.name}</p>
            <p className="text-gray-500">{user.email}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

2. Preventing Request Waterfalls (Sequential Fetching)

A request waterfall occurs when your code awaits one fetch request to finish before starting the next one. This extends page rendering times:

// Sequential Fetching (Slow: Waterfall)
const user = await getUser(userId);
const posts = await getPosts(user.id); // Waits for getUser to complete

If the requests are independent, fetch them in parallel instead.


3. Parallel Data Fetching

To execute multiple independent fetch requests simultaneously, use Promise.all:

import { getUser, getRecentOrders } from "@/lib/api";

export default async function ProfilePage({ params }: { params: Promise<{ id: string }> }) {
  const resolvedParams = await params;
  const userId = resolvedParams.id;

  // Initiate both fetch requests in parallel
  const userPromise = getUser(userId);
  const ordersPromise = getRecentOrders(userId);

  // Wait for both promises to resolve
  const [user, orders] = await Promise.all([userPromise, ordersPromise]);

  return (
    <div className="p-8">
      <h1>Profile: {user.name}</h1>
      <h2>Orders Count: {orders.length}</h2>
    </div>
  );
}

This pattern reduces total fetch wait time to the duration of the single slowest request.

Published on Last updated: