-
Notifications
You must be signed in to change notification settings - Fork 0
services cache
io.cache is an application-governed response cache for GET requests, attached and on by default. It is not an HTTP cache: it does not read Cache-Control or max-age. TTLs come from your call, the app decides what to store, and the app decides when to sweep.
import io from 'double-meh';
const a = await io.get('https://example.com/users/42');
const b = await io.get('https://example.com/users/42');
// second call is served from the cache, no network requestGETs are cached by default; cache: false opts a request out. Use cache: {ttl} (milliseconds) to override the default TTL, or cache: true to force caching for a request the default predicate excludes. Hard gates apply regardless: only GET is ever cached (cache: true on a POST does nothing), and stream: true or bust requests are never stored.
await io.get('https://example.com/c'); // cached with the default TTL
await io.get('https://example.com/c', null, {cache: {ttl: 30_000}});
await io.get('https://example.com/c', null, {cache: false}); // straight to the networkio.cache.theDefault — a boolean or a predicate (options) => boolean — decides which requests are cached when they don't say. The shipped default is options => !options.transport: every GET on the default transport.
io.cache.theDefault = options => options.url.startsWith('https://api.example.com/');io.cache.defaultTtl is 5 minutes. Each entry stores an absolute expiresAt; a fresh entry is served directly, an expired one is refetched. Pass ttl: Infinity to keep an entry until you evict it, or ttl: 0 to force a refetch every time.
io.cache.defaultTtl = 60_000;
await io.get('https://example.com/t', null, {cache: {ttl: 0}}); // always refetchedWhen a stale entry carries an ETag or Last-Modified, the next read attaches If-None-Match / If-Modified-Since. A 304 Not Modified reuses the stored body, merges the 304's headers into the stored entry (all but content-length), and refreshes the entry's expiresAt — no re-download.
await io.get('https://example.com/r', null, {cache: {ttl: 0}});
// stale entry with ETag "v1" → conditional request → 304 → cached body reused
const body = await io.get('https://example.com/r', null, {cache: {ttl: 0}});Only ok (2xx) responses are ever stored; error responses pass straight through.
io.cache.remove(pattern) evicts by:
- an exact URL string,
- a trailing-
*prefix string, - a
RegExptested against the stored key, - a function
(key) => boolean.
await io.cache.remove('https://example.com/users/42'); // exact
await io.cache.remove('https://example.com/users/*'); // prefix
await io.cache.remove(/\/users\//); // RegExp on the key
await io.cache.remove(key => key.includes('draft')); // predicateio.cache.clear() drops everything. io.cache.sweep() deletes only expired entries — it is app-called, nothing runs it for you, so call it on your own schedule (e.g. an interval or idle callback).
await io.cache.clear();
await io.cache.sweep(); // purge entries past their expiresAtThe backend lives at io.cache.storage and defaults to an in-memory Map. Any object implementing get / set / delete / clear / keys (sync or async) can replace it — for example a persistent or shared store.
io.cache.storage = {
get: key => myStore.read(key),
set: (key, entry) => myStore.write(key, entry),
delete: key => myStore.drop(key),
clear: () => myStore.reset(),
keys: () => myStore.list()
};io.cache.save(target, response, ttl?) stores a response directly. io.adopt uses it under the hood, so an adopted GET is durable past its in-flight window and later reads hit without a network call.
await io.adopt('https://example.com/cf', json({from: 'prefetch'}));
const data = await io.get('https://example.com/cf'); // no networkThe main entry attaches the cache for you. io.cache.detach() removes the service; io.cache.attach() reinstalls it. Both maintain io.cache.isActive and return io for chaining.
io.cache.detach();
io.cache.attach();Guides
Concepts
Services
Modules
- fetch transport
- keys & URLs
- helpers
- records
- sse
- storage backends
- compression encoders
- Service Worker integration
Cookbook