My page awaits getUser(), then getOrders(), then getStats() one after another and TTFB is slow. How do I fetch in parallel and still stream the slow part?
#nextjs#performance#data-fetching
DEV3 Developer Forum
Every post is reviewed first. Usually reviewed within 24 hours.
My page awaits getUser(), then getOrders(), then getStats() one after another and TTFB is slow. How do I fetch in parallel and still stream the slow part?
#nextjs#performance#data-fetching
Sequential awaits create a waterfall: total time is the sum. Start independent requests together.
export default async function Page() {
const [user, orders, stats] = await Promise.all([
getUser(),
getOrders(),
getStats(),
]);
return <Dashboard user={user} orders={orders} stats={stats} />;
}
Use Promise.allSettled if one failure should not take down the page.
export default async function Page() {
const statsPromise = getStats(); // kicked off
const user = await getUser(); // needed for the heading
return (
<>
<h1>{user.name}</h1>
<Suspense fallback={<Skeleton />}>
<Stats promise={statsPromise} />
</Suspense>
</>
);
}
async function Stats({ promise }: { promise: Promise<StatsData> }) {
const s = await promise;
return <p>{s.total}</p>;
}
Each async component inside its own <Suspense> fetches in parallel with siblings. Nested components that fetch only after their parent has rendered still waterfall, so hoist a fetch up or preload.
Wrap shared loaders with React cache() so generateMetadata, layout and page can call getUser() without hitting the database repeatedly.
When query B needs A's result, do the join in the database (one query, include/$lookup) rather than two round trips.
Pitfall: await inside a for loop over ids is also a waterfall. Use Promise.all(ids.map(...)) or one $in query, with a concurrency cap for large arrays.