The Frontend Made 42 API Calls Per Page
performance api-design
System Design Scenario
The Frontend Made 42 API Calls Per Page
When REST meets reality - sequential requests create waterfall delays that drown performance
The user clicks “Dashboard” at 2:14 PM. Eight seconds later, the page finally loads. Eight seconds. In 2024. The backend team insists their APIs are lightning fast - each endpoint responds in under 50 milliseconds. They’re not wrong. But the frontend is making 42 separate requests, one after another, like a person asking questions in a meeting but waiting for each answer before asking the next one.
It started innocently enough. The user object needed profile data, so one API call. The profile needed subscription status, so another call. The subscription needed payment history, so a third call. Each feature team built their own endpoints, and the frontend dutifully called them all. What began as clean separation of concerns became a waterfall of dependencies that turns a snappy backend into a sluggish user experience.
Think of it like ordering coffee where you have to visit different counters: first the order counter, wait for receipt, then payment counter, wait for payment confirmation, then brewing counter, wait for brewing to start, then pickup counter. Each step is fast, but the serial process is glacial. This is the API waterfall problem.
Why This Happens
The instinct is to think in terms of resources and endpoints - users have profiles, profiles have subscriptions, subscriptions have payments. This creates a natural dependency chain where each API response contains only the ID needed to make the next request.
REST APIs encourage this pattern through resource-oriented design. A user endpoint returns user_id. A profile endpoint requires user_id and returns subscription_id. A subscription endpoint requires subscription_id and returns payment_history_ids. Each endpoint is clean, focused, and fast - but chaining them together creates sequential dependencies.
Page load starts
-> GET /users/me (50ms)
-> GET /profiles/{userId} (45ms)
-> GET /subscriptions/{profileId} (60ms)
-> GET /payments/{subscriptionId} (55ms)
-> GET /notifications/{userId} (40ms)
-> Total time: 250ms + network overhead = 800ms+
The problem compounds with network latency. Each API call includes not just server processing time, but also TCP handshake, DNS lookup, SSL negotiation, and network transit time. At 100ms round-trip latency, 42 sequential calls take 4.2 seconds in network time alone, before any server processing.
API response time and page load time are different metrics - sequential calls multiply latency, not add it.
The Naive Solution (and where it breaks)
Most teams reach for parallel requests or client-side caching. The thinking is that making calls simultaneously instead of sequentially will reduce the total time.
Parallel requests help when dependencies allow it, but many calls have genuine dependencies. You can’t fetch subscription details until you have the user profile. You can’t load payment history until you have the subscription ID. The dependency chain forces serialization regardless of your parallel execution strategy.
Client-side caching seems smarter - cache API responses and reuse them across page loads. But caching doesn’t help the initial page load, which is where users form their performance perception. Additionally, cache invalidation becomes complex when you have 42 different endpoints with different update frequencies.
Parallel requests only help when calls are truly independent - dependency chains still serialize.
Premium Content
Unlock the full article along with everything else in the archive — all in one place.