Data Fetching in Server Components
Next.js 15's most powerful feature: Server Components can be async functions. You can connect directly to databases or APIs — nothing runs in the browser, the code stays on the server.
// app/blog/page.tsx — This is a Server Component
export default async function BlogList() {
// This runs on the SERVER — API keys stay safe
const response = await fetch("https://api.example.com/posts");
const posts = await response.json();
return (
<div>
{posts.map((post: any) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
Static vs Dynamic Rendering
| Mode | Description | When? |
|---|---|---|
| Static | Rendered at build time, cached on CDN | Blog, landing pages, static content |
| Dynamic | Rendered on each request on the server | Dashboard, user-specific content |
| ISR | Regenerated at a set interval | News sites, price lists |
Cache Control
// 1. Static — cached at build time (default)
const data = await fetch("https://api.com/data");
// 2. Dynamic — NO cache, fetched on every request
const data = await fetch("https://api.com/data", {
cache: "no-store",
});
// 3. ISR — revalidate every 60 seconds
const data = await fetch("https://api.com/data", {
next: { revalidate: 60 },
});
Parallel Data Fetching
export default async function Dashboard() {
// ✅ Parallel — they don't wait for each other
const [user, orders, stats] = await Promise.all([
fetch("/api/user").then(r => r.json()),
fetch("/api/orders").then(r => r.json()),
fetch("/api/stats").then(r => r.json()),
]);
return <div>...</div>;
}
Skeleton Loading with loading.tsx
// app/blog/loading.tsx
export default function Loading() {
return (
<div className="space-y-4">
{[...Array(3)].map((_, i) => (
<div key={i} className="animate-pulse">
<div className="h-6 bg-gray-200 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
</div>
))}
</div>
);
}
Önemli Noktalar
- Server Components can be async — use await directly
- Control cache with no-store (dynamic) or revalidate (ISR)
- Use Promise.all for parallel fetching — never fetch serially
- loading.tsx creates an automatic Suspense boundary