Data Fetching in Server Components

await fetch — That's the Whole API

Data Fetching in Server Components

Server Components can `await` directly. There is no SWR or React Query at the server boundary — just async functions and `fetch`.

4 min read Level 2/5 #nextjs#data-fetching
What you'll learn
  • Await `fetch` in an async Server Component
  • Compose multiple awaits cleanly
  • Use `Promise.all` for parallel requests

Because Server Components run on the server, they can be async functions. The data fetching API is just await plus whatever client you like — fetch, a database driver, an ORM, anything Node.js can run.

Awaiting fetch Directly

type Post = { id: string; title: string; body: string }

export default async function PostPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const post: Post = await fetch(`https://api.example.com/posts/${id}`).then(r => r.json())
  return <article><h1>{post.title}</h1><p>{post.body}</p></article>
}

No useEffect, no loading state in the component. If the fetch throws, Next routes the error to the nearest error.tsx.

Parallel Requests

Awaiting one after another serializes them. Use Promise.all to fire requests in parallel.

export default async function Dashboard() {
  const [user, posts] = await Promise.all([
    fetch('/api/me').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
  ])
  return <Layout user={user} posts={posts} />
}

Total latency is now the slower of the two, not the sum.

Direct Database Access

You do not have to go through HTTP. A Server Component can talk to a database client directly because it runs server-side.

import { db } from '@/lib/db'

export default async function Posts() {
  const posts = await db.posts.findMany({ orderBy: { createdAt: 'desc' } })
  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}
The Extended fetch API →