October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

Avoid GraphQL Waterfalls in Next.js App Router with Suspense

Learn how to start independent GraphQL requests early, place Suspense boundaries for useful streaming, and diagnose backend batching issues separately in Next.js App Router.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To avoid an unnecessary GraphQL waterfall in the Next.js App Router, start independent requests before awaiting their results, then use Suspense boundaries to stream the page regions that depend on pending data. Suspense controls when UI can render; it does not make a later request start earlier if your code waits for an earlier request first.

Find the dependency before changing the code

Map the page’s data needs as a dependency graph. Mark which GraphQL operations can run without another result and which need an ID, token, or other value returned by earlier work. This distinction determines whether requests should overlap.

As an Amazon Associate I earn from qualifying purchases.

  • Independent: two panels need separate data and neither query needs the other’s result. Start both as early as possible.
  • Dependent: the second query needs an identifier returned by the first. Preserve that sequence; launching it earlier is not possible without changing the data flow.

Next.js describes parallel data fetching as eagerly starting independent requests. Sequential await statements can serialize operations that otherwise could overlap. Next.js documents parallel and sequential data fetching.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Start independent operations before awaiting them

When the page needs all results together, create the promises first and wait for them as a group:

const profilePromise = getProfile(userId);
const recommendationsPromise = getRecommendations(userId);

const [profile, recommendations] = await Promise.all([
  profilePromise,
  recommendationsPromise,
]);

Both calls begin before the code waits for either result. By contrast, awaiting getProfile before calling getRecommendations makes the second operation wait, even if it does not depend on the profile response.

If each region can appear as soon as its own data arrives, avoid joining the results into a single wait. Keep the regions in independently renderable components so one slow query does not hold back unrelated content. A real dependency is different: when the second operation needs the first operation’s output, keep the sequence explicit rather than forcing artificial parallelism. Next.js explains when parallel versus sequential fetching applies.

Use Suspense to stream the parts that are waiting

Place a Suspense boundary around the component whose data may still be pending, and provide a fallback that makes sense for that part of the page. Content outside the boundary can render while the suspended component waits; separate boundaries let independent regions become visible at different times.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { Suspense } from 'react';

export default function Page() {
  return (
    <>
      <PageHeading />
      <Suspense fallback={<ProfileSkeleton />}>
        <ProfilePanel />
      </Suspense>
      <Suspense fallback={<RecommendationsSkeleton />}>
        <RecommendationsPanel />
      </Suspense>
    </>
  );
}

In an App Router Server Component, a component that awaits its data can suspend so its boundary displays the fallback while the result is pending. In Client Components, the data library must use a Suspense-aware mechanism for the same pattern. Consult the Next.js loading UI and streaming documentation for the current behavior and route conventions.

Suspense is not a concurrency switch. If component B starts its query only after query A resolves, wrapping B in Suspense can show a fallback during that wait, but it will not cause B’s query to begin sooner. Fix request initiation order separately from boundary placement.

Choose boundary placement for the work that can block

A route-segment loading.js provides loading UI for navigation and streaming at that segment. It does not necessarily help if runtime or uncached work in a layout blocks before that same-segment loading UI can appear. In that case, isolate the work with a nearer Suspense boundary or, where the page structure permits, move it from the layout into the page. The two approaches address different placement problems: route-level loading UI covers a segment, while a local boundary can cover specific pending work. Next.js describes this distinction in its streaming guidance.

Choose an Apollo pattern that matches where the data belongs

Apollo’s official Next.js App Router integration covers both React Server Components and Client Components. Use its current package setup and cache-boundary guidance rather than combining patterns by guesswork. The integration documents a shared Apollo client instance for a single server request to help avoid duplicate work, suspense-enabled hooks such as useSuspenseQuery, and PreloadQuery for starting a query in a Server Component before a Client Component consumes it. See Apollo’s Next.js integration guide.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Use server-side preloading when a Server Component should initiate a query before handing work to a Client Component.
  • Use a suspense-enabled client hook when the data belongs in a Client Component and that component should suspend while it is pending.
  • Follow Apollo’s cache-boundary and request-scoped client setup. Avoid overlapping Server Component and SSR queries unless the duplication is intentional and understood.
  • As Apollo advises, treat data preloaded for a Client Component as client data; do not assume that preloading changes the data’s ownership or removes the need to follow the integration’s cache guidance.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Keep request scheduling separate from backend batching

Parallelizing page-level GraphQL operations does not fix an N+1 problem inside a GraphQL operation. A resolver may still issue repeated data-source calls for related records. Conversely, batching resolver loads does not make sequential operations in the React tree start at the same time.

Apollo recommends DataLoader for batching, deduplication, and caching at the data-source layer. Its memoization is scoped to a GraphQL request, so it is not a general cross-request cache. Use it when repeated resolver loads are the problem, and diagnose route-level request scheduling separately. See Apollo’s data-fetching guidance.

Verify which layer is causing the wait

Use request traces to check when each GraphQL operation begins and ends, and inspect resolver or database activity separately. Then test the page under production-like rendering and the actual deployment runtime: caching, errors, and streaming behavior can affect what users see. No universal speedup follows from adding Suspense or changing promise structure; the outcome depends on the dependency graph and where the delay occurs.

  • If independent operations begin one after another, move their initiation earlier and await them together or render their regions independently.
  • If the second operation needs the first result, keep that dependency and consider whether the dependent work can be isolated behind a boundary.
  • If operations overlap but one region still waits, review boundary placement and the component’s Suspense behavior.
  • If one GraphQL operation shows repeated resolver or data-source loads, investigate batching such as DataLoader rather than changing React request scheduling.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.