Data fetching și streaming
Date direct în Server Components, paralel, Suspense și params ca Promise.
Actualizat
Ce înseamnă data fetching în Next
Data fetching = aducerea datelor (din DB, dintr-un API) de care are nevoie pagina. În Next App Router, locul implicit e Server Component-ul: o funcție async care citește datele direct și randează rezultatul.
export default async function Page() {
const posts = await db.post.findMany() // ORM, SQL, fetch — orice
return <PostList posts={posts} />
}Comparat cu varianta clasică React (useEffect + fetch + useState): fără loading state pe client, fără API route intermediar, fără secrete expuse, fără waterfall client → server → DB.
Moduri de a aduce date — și când
| Mod | Unde | Când |
|---|---|---|
await direct în Server Component |
server | implicit — orice afișare de date |
Promise trimis la Client Component + use() |
server pornește, client citește | componenta client are nevoie de date, dar vrei streaming |
| TanStack Query | client | date foarte interactive (polling, infinite scroll) — Server state |
| Route Handler | server | API pentru alții / webhooks — nu pentru propriile pagini |
useEffect + fetch |
client | aproape niciodată |
Paralel, nu cascadă
Waterfall = request-uri care așteaptă unul după altul fără motiv.
// ✗ waterfall — 2 × timpul
const user = await getUser(id)
const posts = await getPosts(id)
// ✓ paralel
const [user, posts] = await Promise.all([getUser(id), getPosts(id)])Ce este streaming
Fără streaming, userul vede pagina abia când toate datele sunt gata. Cu streaming, serverul trimite HTML-ul pe bucăți: ce e gata apare imediat, părțile lente vin când termină.
Instrumentul: <Suspense>.
export default function Page() {
return (
<>
<Header /> {/* apare imediat */}
<Suspense fallback={<Skeleton />}>
<Recommendations /> {/* async, apare când e gata */}
</Suspense>
</>
)
}loading.tsx = un <Suspense> automat în jurul întregii pagini.
Promise trimis la client
Pornești request-ul pe server, dar îl citești într-un Client Component:
// server
const statsPromise = getStats() // fără await — nu blochează
<Suspense fallback={<Spinner />}>
<Stats data={statsPromise} />
</Suspense>
// client
'use client'
import { use } from 'react'
export function Stats({ data }: { data: Promise<Stats> }) {
const stats = use(data) // suspendă până vine
}Date de runtime
cookies(), headers(), searchParams, params (pentru valori nepre-generate) depind de request — nu pot fi știute la build. Componentele care le citesc devin dinamice și trebuie puse în <Suspense>, ca restul paginii să rămână static. Exact așa e afișat progresul tău în aplicația asta.
Deduplicare
Aceeași funcție chemată din layout + page + generateMetadata?
| Unealtă | Durată |
|---|---|
cache() din React |
pe durata unui request |
'use cache' |
persistent, între request-uri — lecția Cache |
Pe scurt
- Date → direct în Server Components, cu
await. Promise.allpentru request-uri independente.<Suspense>pentru streaming; datele de runtime stau în Suspense.