LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2918 min read

Data Fetching in Next.js

Learn the core patterns for fetching data in Server Components — the foundation every rendering strategy builds on.

Fetching Directly in Server Components

Because Server Components can be async functions, you fetch data with a plain await, right where you use it — no useEffect, no loading state variable, no separate data-fetching library required for the basic case.

export default async function OrdersPage() {
const res = await fetch('https://api.example.com/orders');
const orders = await res.json();
return (
<ul>
{orders.map(o => <li key={o.id}>{o.item}</li>)}
</ul>
);
}

Sequential vs Parallel Fetching

Awaiting one fetch after another runs them sequentially, adding their times together. When requests don't depend on each other, start them together and await both, so they run in parallel instead.

Sequential (slower)

tsx
const user = await getUser(id); // waits
const orders = await getOrders(id); // then waits again
// total time ≈ getUser + getOrders

Parallel (faster)

tsx
const [user, orders] = await Promise.all([
getUser(id),
getOrders(id),
]);
// total time ≈ the SLOWER of the two, not the sum

Fetching Without an API — Direct Database Access

Because Server Components run only on the server, they can talk directly to a database — no API endpoint required as a middleman, since this code never reaches the browser.

import { db } from '@/lib/db';
export default async function UsersPage() {
const users = await db.user.findMany();
return (
<ul>
{users.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
);
}
A Genuine Full-Stack Shortcut

This is one of the biggest practical wins of Server Components: a page can query a database directly, skipping the API layer entirely for internal data needs.

Passing Fetched Data to Client Components

Fetch once in the Server Component, then pass the result down as a prop to any Client Component that needs to make it interactive — this avoids duplicating the fetch on the client.

// page.tsx (Server Component)
import FilterableList from './FilterableList'; // Client Component
export default async function Page() {
const items = await getItems();
return <FilterableList items={items} />;
}

Common Beginner Mistakes

Awaiting independent fetches one by one

This adds their durations together — use Promise.all for requests that don't depend on each other's results.

Re-fetching the same data inside a Client Component that already received it as a prop

This duplicates the request and adds an unnecessary loading state — just use the prop that was already fetched on the server.

FAQs

Less often for initial page data, since Server Components cover that — but they're still valuable for client-side data that needs polling, refetching on focus, or optimistic updates after the page has loaded.

Yes, but you lose Next.js's automatic caching behavior tied to fetch — using the native fetch is what enables the caching and revalidation features covered in the next lesson.

Summary

Server Components let you fetch data with plain async/await, run independent requests in parallel, and even talk to a database directly. Next, you'll learn exactly how Next.js caches these fetch calls.

Next Lesson →

Caching in Next.js