No sequential API request waterfalls on page load
Why it matters
Sequential API requests that could run in parallel add their round-trip times together: if fetching a user profile takes 200ms and fetching their posts takes 150ms, a waterfall loads in 350ms; a parallel fetch completes in 200ms. Across a dashboard that chains four independent requests, this compounds to 600–800ms of avoidable latency before the page renders any data. For server components in Next.js, parallel fetching is a single line change (Promise.all). Request waterfalls are one of the most common and highest-impact performance issues in data-heavy applications, and they scale worse as API latency grows. ISO 25010 performance-efficiency.time-behaviour requires minimizing response time for the same work.
Severity rationale
High because each chained sequential request multiplies the total page load latency by the number of chain links, with real-world impact of hundreds of milliseconds to seconds.
Remediation
Replace await fetch() chains with Promise.all for any requests that don't depend on each other's return values. Identify the true dependency graph: if request B needs an ID from request A, only A must complete first; B and C can still be parallelized.
// Before — sequential waterfall: 200ms + 150ms + 120ms = 470ms total
const user = await fetchUser()
const posts = await fetchPosts(user.id)
const settings = await fetchSettings(user.id)
// After — user first (needed for ID), then posts + settings in parallel
const user = await fetchUser() // 200ms
const [posts, settings] = await Promise.all([
fetchPosts(user.id), // 150ms }
fetchSettings(user.id) // 120ms } run together = 150ms
])
// Total: 200ms + 150ms = 350ms (saves 120ms per request)
// For fully independent requests, parallelize everything
const [products, categories, featuredBanner] = await Promise.all([
fetchProducts(),
fetchCategories(),
fetchFeaturedBanner()
])
Detection
-
ID:
no-waterfall-requests -
Severity:
high -
What to look for: For each page that fetches data, trace the data fetching pattern on page load. Count the number of sequential fetch calls (where request B waits for request A to complete before starting, and request B does not depend on A's response data). Identify chains of 2+ sequential requests where at least 2 could run in parallel via
Promise.all. Report: "X pages fetch data, Y have waterfall patterns with Z parallelizable requests." -
Pass criteria: No more than 1 page has a waterfall pattern with 2+ parallelizable requests. If a page needs 3+ pieces of data, at least 2 are fetched concurrently via
Promise.all,Promise.allSettled, or equivalent parallel pattern. Report: "X of Y data-fetching pages use parallel requests where possible." -
Fail criteria: 2 or more pages have waterfall request patterns where requests are chained sequentially despite having no data dependency between them, adding unnecessary latency.
-
Skip (N/A) when: No data fetching is detected in the project (0
fetch(),axios,useSWR,useQuery,getServerSideProps, or server component data fetching patterns found). -
Detail on fail:
"3 of 5 data-fetching pages have waterfalls — dashboard chains 3 independent requests sequentially (user, settings, posts) instead of Promise.all"or"Product page fetches product, then related products, then reviews one at a time — 3 sequential requests that could be 1 parallel batch" -
Remediation: Parallelize requests:
// Before — waterfall (sequential) const user = await fetch('/api/user').then(r => r.json()) const settings = await fetch(`/api/user/${user.id}/settings`).then(r => r.json()) const posts = await fetch(`/api/user/${user.id}/posts`).then(r => r.json()) // After — parallel (concurrent) const [user, settings, posts] = await Promise.all([ fetch('/api/user').then(r => r.json()), fetch('/api/user/settings').then(r => r.json()), fetch('/api/user/posts').then(r => r.json()) ])For Next.js with required user ID:
// After — get user first, then parallelize others const user = await fetch('/api/user').then(r => r.json()) const [settings, posts] = await Promise.all([ fetch(`/api/user/${user.id}/settings`).then(r => r.json()), fetch(`/api/user/${user.id}/posts`).then(r => r.json()) ])
External references
- iso-25010:2011 · performance-efficiency.time-behaviour — Time Behaviour
Taxons
History
- 2026-04-18·v1.0.0·Initial import from performance-load·automated