# No sequential API request waterfalls on page load

- **Pattern:** `ab-002069` (`performance-load.rendering.no-waterfall-requests`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002069
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002069)

## 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.

```ts
// 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:

  ```ts
  // 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:

  ```ts
  // 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 performance-efficiency.time-behaviour

Taxons: performance

HTML version: https://auditbuffet.com/patterns/ab-002069
