RORK LABJP
PRICE — Rork Max spans $200 to $1,800 per month, with the upper tiers aimed at heavier builders and teamsFREE — The free tier lands at roughly five prompts per week, enough to try it but not to build on continuouslySHIP — App Store publishing is automated through builds, certificates, and submission, so you can ship an iOS app without a Mac or XcodeSIM — A browser-streamed simulator lets you watch your app run in a real Apple environment from your own browserNATIVE — It reaches HealthKit, ARKit and LiDAR, NFC, Dynamic Island, and Metal 3D — territory React Native cannot touchFUNDING — Rork raised a $15M seed led by Left Lane Capital, announced April 9, 2026, and acquired app builder PaperlinePRICE — Rork Max spans $200 to $1,800 per month, with the upper tiers aimed at heavier builders and teamsFREE — The free tier lands at roughly five prompts per week, enough to try it but not to build on continuouslySHIP — App Store publishing is automated through builds, certificates, and submission, so you can ship an iOS app without a Mac or XcodeSIM — A browser-streamed simulator lets you watch your app run in a real Apple environment from your own browserNATIVE — It reaches HealthKit, ARKit and LiDAR, NFC, Dynamic Island, and Metal 3D — territory React Native cannot touchFUNDING — Rork raised a $15M seed led by Left Lane Capital, announced April 9, 2026, and acquired app builder Paperline
Articles/Dev Tools
Dev Tools/2026-07-24Advanced

Collapsing Duplicate Requests Into One: A Reference-Counted Single-Flight Layer

When several components fire the same API call at launch, you get a burst of identical requests. Here is a single-flight layer that shares one in-flight promise instead: how to build the key that decides the folding, the trap of handing out a failed promise forever, the trap of one caller's abort cancelling everyone, and the test that keeps it all from regressing, with the real network numbers alongside.

Rork532React Native224API7Performance25Architecture22

Premium Article

Late one night I attached a network inspector to a freshly released wallpaper app and opened it. Rows with the same URL stacked up one after another. /me/entitlements, four times inside roughly 200ms. Same payload, same JSON coming back.

Something tightened in my chest. Four times the wasted traffic would have been bad enough, but right after three concurrent 401s hit, a handful of users were being logged out for no clear reason. Three requests noticed the expired token at the same moment, each fired its own refresh, and the one that finished last invalidated the token the earlier one had already issued.

The cause was not a single screen. Home, the favorites badge, the paywall gate — separate components were each running the same fetch independently, "because I need it." None of them knew the one next door was already reaching for the same thing.

What I want to walk through is the design that folds these simultaneous, identical requests into a single in-flight one. It comes from the code I actually run across six apps on one shared networking base, added the morning after that night. The mechanism is not complicated. But written naively, it has two traps you will always hit, and it only becomes usable once you clear both.

Why the same request fires several times

In a React Native app, each component's useEffect runs the moment the screen mounts. When several components that need the same data appear at once, each issues its own fetch.

The fetch code Rork generates usually lives entirely inside a component. It looks like this.

function useEntitlements() {
  const [data, setData] = useState<Entitlements | null>(null);
  useEffect(() => {
    fetch(`${API}/me/entitlements`, {
      headers: { Authorization: `Bearer ${token}` },
    })
      .then((r) => r.json())
      .then(setData);
  }, []);
  return data;
}

Call this hook from three components and fetch runs three times. Each hook sees only its own local state; there is nowhere shared that answers "is the same fetch already in flight?"

My first thought was to add a cache. But on the very first launch the cache is empty. Everyone decides "there is nothing cached" at nearly the same instant and all rush to fetch. The cache fills only after everyone has already sent, too late to help. What needed protecting was not "storing the result" but "sharing the single in-flight request."

The core of single-flight: share the in-flight promise

There is one thing to do. If a fetch for a given key is already in flight, do not issue a new fetch — return the in-flight promise as is. This is single-flight, or request coalescing.

The minimal form is just this.

const inflight = new Map<string, Promise<unknown>>();
 
function coalesce<T>(key: string, run: () => Promise<T>): Promise<T> {
  const existing = inflight.get(key);
  if (existing) return existing as Promise<T>;
 
  const promise = run();
  inflight.set(key, promise as Promise<unknown>);
  return promise;
}

The key is a string that uniquely names what you are fetching. Combining method and path, like GET /me/entitlements, reads clearly. Called four times with the same key, run() actually runs only the first time.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A single-flight layer that collapses duplicate GETs at startup by sharing one in-flight promise
The trap where a rejected promise keeps getting handed to every future caller, and why you must delete on settle
A reference-counted AbortController so one caller's unmount does not cancel the shared request for everyone
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-08-01
Every Experiment Kept Landing on the Same Devices: Hash Choice and Salt Position, Measured Across a Million IDs
On-device experiment assignment looked fine in isolation, then collapsed the moment two experiments ran side by side. Here is what one million IDs revealed about four hash functions, why the culprit was the concatenation order rather than hash quality, and the implementation I settled on, with the measured numbers.
Dev Tools2026-07-24
Resolving App Config in Three Layers: Merging Defaults, User, and Remote With Bounded Overrides
A single type-safe layer that merges compiled defaults, user preferences, and remote config. So a broken remote value never takes your app down, each key gets its own override strength, plus schema validation and range clamping, built from a real production incident.
Dev Tools2026-07-14
Designing Seams That Survive AI Regeneration in Rork
Every follow-up prompt to Rork can quietly wipe out logic you wrote by hand. Protecting it with prompts is a patch, not a fix. Here is how to separate generated code from code you own, and draw a boundary that regeneration cannot reach, with working Zustand and service-layer examples.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →