-
Notifications
You must be signed in to change notification settings - Fork 0
Cookbook: deduping and prefetch
The io.track service collapses concurrent identical GETs into a single in-flight request and lets you register interest in a URL before anyone fetches it. It is attached and active by default — no configuration needed for the common case.
Fire the same GET from several places at once and only one network request goes out; every caller shares the one decoded envelope. This is automatic for GETs.
import io from 'double-meh';
const [a, b, c] = await Promise.all([
io.get('https://api.example.com/me'),
io.get('https://api.example.com/me'),
io.get('https://api.example.com/me')
]);
// one request; a, b, and c are the same dataKeys are canonicalized, so query order does not matter — ?a=1&b=2 and ?b=2&a=1 dedupe together.
Tracking is GET-only — sharing one decoded envelope is only sound for safe reads, so track: true on another verb does nothing. Opt a specific GET out with track: false, or narrow the default set with io.track.theDefault (a boolean or a predicate over the options; the shipped default skips requests with an explicit transport). Streaming requests (stream: true) are never deduped.
await io.get('https://api.example.com/now', null, {track: false}); // always hit the network
io.track.theDefault = options => !options.transport && !options.meta?.noDedup; // narrow the defaulttrack: 'wait' returns a promise that resolves when a real request for the same URL lands later — but never issues a request itself. Use it to subscribe to data that another part of the app is about to fetch.
const waiter = io.get({url: 'https://api.example.com/profile', track: 'wait'});
// nothing has been sent yet...
const real = io.get('https://api.example.com/profile'); // this one actually fires
const [subscribed, fetched] = await Promise.all([waiter, real]);
// both resolve with the same landed datatrack: 'wait' needs a trackable request: using it on a non-GET throws a TypeError.
io.adopt(url, response) fulfills the tracked entry for a URL from a Response (or a promise of one) you already have — a server-rendered payload, a push message, a preload. A later io.get of that URL resolves from the adopted response instead of hitting the network.
// e.g. a payload the page was bootstrapped with
io.adopt(
'https://api.example.com/me',
new Response(JSON.stringify(window.__ME__), {headers: {'content-type': 'application/json'}})
);
const me = await io.get('https://api.example.com/me'); // resolves from the adopted responseSince the cache is on by default for GETs, adopt also stores the response in io.cache, so plain io.get calls keep resolving from the adopted body even after the in-flight entry is gone — until the TTL expires or the entry is evicted.
Guides
Concepts
Services
Modules
- fetch transport
- keys & URLs
- helpers
- records
- sse
- storage backends
- compression encoders
- Service Worker integration
Cookbook