Data Fetching Patterns

Back

Loading concept...

Data Fetching Patterns in Next.js

The Restaurant Kitchen Analogy 🍳

Imagine you’re running a busy restaurant. Your kitchen has different ways to prepare orders:

  • Sometimes the chef preps everything before the waiter arrives
  • Sometimes the waiter asks the kitchen mid-service
  • Sometimes multiple dishes cook at once
  • Sometimes dishes must be made one after another

Next.js data fetching works exactly like this kitchen!


1. Server vs Client Fetching

What’s the Difference?

Think of two different workers at a restaurant:

Server Fetching = The Kitchen (Back of House)

  • Work happens before food leaves the kitchen
  • Customer never sees the preparation
  • Faster for the customer—food arrives ready!

Client Fetching = The Waiter (Front of House)

  • Work happens at the table
  • Customer watches and waits
  • More interactive—can change orders live!

Server Fetching Example

The kitchen prepares everything before serving:

// This runs on the SERVER
// Customer never sees this code run!

async function MenuPage() {
  const dishes = await fetch(
    'https://api.restaurant.com/menu'
  );
  const menu = await dishes.json();

  return (
    <ul>
      {menu.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Why use Server Fetching?

  • Page loads faster (data already there!)
  • Better for search engines (Google sees the content)
  • Secrets stay hidden (API keys never reach browser)

Client Fetching Example

The waiter brings ingredients to the table:

'use client'
import { useState, useEffect } from 'react';

function LiveOrders() {
  const [orders, setOrders] = useState([]);

  useEffect(() => {
    // This runs in the BROWSER
    fetch('/api/orders')
      .then(res => res.json())
      .then(data => setOrders(data));
  }, []);

  return <OrderList orders={orders} />;
}

Why use Client Fetching?

  • Needs user interaction (button clicks, typing)
  • Data changes constantly (live updates)
  • User-specific data (after page loads)
graph TD A["User Visits Page"] --> B{What kind of data?} B -->|Static/SEO needed| C["Server Fetch"] B -->|Interactive/Live| D["Client Fetch"] C --> E["Fast First Load!"] D --> F["Updates in Real-Time!"]

2. Parallel Data Fetching

The Problem

Imagine ordering pizza AND salad. What if the kitchen made the pizza first, THEN started the salad? You’d wait forever!

The Solution: Cook Everything at Once!

async function DinnerPage() {
  // BAD: Sequential (slow!)
  // const pizza = await getPizza();
  // const salad = await getSalad();
  // Total time: pizza + salad

  // GOOD: Parallel (fast!)
  const [pizza, salad] = await Promise.all([
    getPizza(),
    getSalad()
  ]);
  // Total time: whichever takes longer

  return (
    <div>
      <PizzaCard pizza={pizza} />
      <SaladCard salad={salad} />
    </div>
  );
}

Visual Comparison

graph TD subgraph Sequential ["Sequential: SLOW"] A1["Start"] --> A2["Fetch Pizza: 2s"] A2 --> A3["Fetch Salad: 1s"] A3 --> A4["Done: 3 seconds!"] end subgraph Parallel ["Parallel: FAST"] B1["Start"] --> B2["Fetch Pizza: 2s"] B1 --> B3["Fetch Salad: 1s"] B2 --> B4["Done: 2 seconds!"] B3 --> B4 end

Golden Rule: If two things don’t depend on each other, fetch them together!


3. Sequential Data Fetching

When Order Matters

Sometimes you MUST wait. Like at a restaurant:

  1. First, check if table is available
  2. THEN take the order
  3. THEN cook the food

You can’t cook before knowing what they want!

Real Example: User Profile Then Posts

async function ProfilePage({ userId }) {
  // Step 1: Get the user first
  const user = await getUser(userId);

  // Step 2: Now we can get THEIR posts
  const posts = await getPosts(user.id);

  return (
    <div>
      <h1>{user.name}</h1>
      <PostList posts={posts} />
    </div>
  );
}

Why Sequential Sometimes?

graph TD A["Get User"] --> B{User Found?} B -->|Yes| C[Get User's Posts] B -->|No| D["Show Error"] C --> E["Display Everything"]

Use Sequential when:

  • Step 2 needs data from Step 1
  • You need to check permissions first
  • Data depends on previous results

4. Colocated Data Fetching

The Smart Kitchen Layout

In a great restaurant kitchen, ingredients are placed right where they’re needed:

  • Pasta station has pasta, sauce, cheese nearby
  • Grill station has meats, seasonings, tools nearby

No running around the kitchen!

What is Colocation?

Put the data fetching RIGHT NEXT TO the component that uses it!

OLD Way (Data passed down):

// Page fetches EVERYTHING
async function Page() {
  const user = await getUser();
  const posts = await getPosts();
  const comments = await getComments();

  return <Profile
    user={user}
    posts={posts}
    comments={comments}
  />;
}

NEW Way (Colocated):

// Each component fetches its OWN data!
async function Page() {
  return (
    <div>
      <UserCard />     {/* Fetches user */}
      <PostList />     {/* Fetches posts */}
      <CommentFeed />  {/* Fetches comments */}
    </div>
  );
}

async function UserCard() {
  const user = await getUser();
  return <div>{user.name}</div>;
}

Why This is Magical ✨

graph TD A["Page Component"] --> B["UserCard"] A --> C["PostList"] A --> D["CommentFeed"] B --> B1["Fetches own user data"] C --> C1["Fetches own posts data"] D --> D1["Fetches own comments"] style B1 fill:#90EE90 style C1 fill:#90EE90 style D1 fill:#90EE90

Benefits:

  • Each component is independent
  • Easy to move components around
  • Code is easier to understand
  • Next.js automatically runs them in parallel!

5. Request Deduplication

The Waiter’s Nightmare

Imagine 5 tables all order the same dish at once. A bad waiter runs to the kitchen 5 times. A smart waiter says:

“Hey chef, I need 5 of the special!”

One trip. Five dishes.

Next.js Does This Automatically!

async function Header() {
  const user = await getUser(); // Request 1
  return <div>Welcome, {user.name}</div>;
}

async function Sidebar() {
  const user = await getUser(); // Same request!
  return <div>Profile: {user.email}</div>;
}

async function Page() {
  return (
    <div>
      <Header />
      <Sidebar />
    </div>
  );
}

Even though we call getUser() twice, Next.js is smart:

graph TD A["Header calls getUser"] --> C{Already fetching?} B["Sidebar calls getUser"] --> C C -->|First time| D["Make ONE request"] C -->|Already in progress| E["Wait for same request"] D --> F["Share result with both!"] E --> F

How It Works

Next.js uses the built-in fetch function and:

  • Remembers identical requests
  • Makes the call only ONCE
  • Shares the result with everyone who asked

You get this for FREE when using fetch!

// These are IDENTICAL requests
// Next.js will only call the API once!

const data1 = await fetch('https://api.example.com/user');
const data2 = await fetch('https://api.example.com/user');

Quick Summary

Pattern When to Use Kitchen Analogy
Server Fetch SEO, secrets, fast load Chef preps in kitchen
Client Fetch Interactive, live data Waiter at table
Parallel Independent data Cook dishes at once
Sequential Data depends on data Check table, then order
Colocated Self-contained parts Ingredients at station
Deduplication Same data many places One order, many plates

You’re Now a Data Fetching Chef! 👨‍🍳

Remember:

  1. Server first = Faster pages
  2. Parallel when possible = Shorter waits
  3. Sequential when needed = Correct order
  4. Colocate data = Cleaner code
  5. Trust deduplication = No wasted requests

Your Next.js kitchen is ready to serve amazing web experiences!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.