Skip to content

Cookbook: caching

Eugene Lazutkin edited this page Jul 1, 2026 · 2 revisions

Cookbook: Caching

double-meh ships an in-memory GET cache that is opt-in per request — attached by default, but it stores nothing until a request asks for it. It also revalidates stale entries with ETags, so a 304 Not Modified reuses the cached body for free.

Attach and detach

The cache is already attached when you import io from 'double-meh'. Call attach() / detach() only to toggle it globally.

import io from 'double-meh';

io.cache.attach(); // no-op by default; the cache is already on
io.cache.detach(); // turn caching off process-wide

Cache a GET

Add cache: true to store the response and serve later identical GETs from memory (default TTL is 5 minutes). Only GETs are ever cached — writes and streams pass straight through.

const a = await io.get('https://api.example.com/config', null, {cache: true});
const b = await io.get('https://api.example.com/config', null, {cache: true});
// second call is served from the cache — no network request

Set a TTL

Pass an object to control freshness. {ttl: 0} stores the entry but forces revalidation on the next read; a large TTL keeps a rarely-changing resource around.

await io.get('https://api.example.com/countries', null, {cache: {ttl: 24 * 60 * 60 * 1000}});

When a stale entry carries an ETag (or Last-Modified), the next read sends a conditional request; a 304 refreshes the TTL and reuses the stored body.

Skip the cache for one request

cache: false bypasses the cache for a single call — handy for a forced refresh even while caching is on globally.

const fresh = await io.get('https://api.example.com/config', null, {cache: false});

Invalidate entries

io.cache.remove(target) accepts three shapes. A plain URL evicts that exact entry; a string ending in * evicts by prefix; a RegExp or a predicate is matched against the cache key, which has the form GET <canonical-url> (query sorted, fragment dropped).

await io.cache.remove('https://api.example.com/users/42');   // exact URL
await io.cache.remove('https://api.example.com/users/*');    // prefix
await io.cache.remove(/\/users\//);                          // RegExp over the key
await io.cache.remove(key => key.includes('draft'));         // predicate over the key

Clear everything or sweep the expired

clear() drops the whole cache; sweep() removes only entries past their TTL, which is useful to run on a timer to reclaim memory.

await io.cache.clear();          // wipe all entries
await io.cache.sweep();          // drop only expired entries

setInterval(() => io.cache.sweep(), 60 * 1000);

See also

Clone this wiki locally