Http Client Cache

RFC 9111 outbound HTTP cache for b.httpClient.request. Stores GET/HEAD responses keyed on (URL, method, sorted Vary-header values), honours Cache-Control directives (no-store, no-cache, private, max-age, s-maxage, must-revalidate, proxy-revalidate, immutable, stale-while-revalidate, stale-if-error), legacy Pragma: no-cache and Expires, computes freshness per RFC 9111 §4.2 (heuristic 10% rule when no explicit lifetime), revalidates with If-None-Match / If-Modified-Since and merges 304 headers into the stored entry (RFC 9111 §5).

Two store backends ship in-the-box: memoryStore (bounded LRU, per-byte and per-entry caps, eviction emits an audit event) and the explicit Store interface (get / set / delete / clear) so operators can wire their own — Redis, filesystem, etc. The memory store handles the common single-process case without pulling in an external dependency. Operators with shared-cache semantics across a fleet wire their own Store against a shared backing service.

Composes through b.httpClient.request({ ..., cache }). Without opts.cache, behaviour is unchanged — zero overhead for callers who don't want caching. Failures inside the cache hot path (store throws, malformed entry, revalidation network error outside stale-if-error) drop silent and the request falls back to the network — caching is never allowed to surface as a request failure. The same audit / observability hooks emit on every cache decision (hit / miss / stale / revalidated / evicted) so operators get end-to-end visibility.

b.httpClient.cache.memoryStore(opts) #

stable0.8.53
{
  maxBytes:       number,   // total stored body bytes; default: 64 MiB
  maxEntries:     number,   // count cap; default: 1024
  evictionPolicy: "lru",    // currently the only policy; reserved
}

In-memory bounded-LRU cache store implementing the Store shape: get(key), set(key, entry), delete(key), clear(). Eviction runs when the byte total or entry count exceeds the configured caps; eviction emits an audit event when an audit sink is wired (via b.httpClient.cache.create({ audit })). Stored values include the response body buffer, so the byte total reflects real memory pressure rather than a rough estimate.

Suitable for single-process workloads. For shared-cache semantics across a fleet, wire your own Store against a shared backing service (Redis, filesystem, etc.) — the same shape applies.

var store = b.httpClient.cache.memoryStore({
  maxBytes:   16 * 1024 * 1024,
  maxEntries: 256,
});
var cache = b.httpClient.cache.create({ store: store });
await b.httpClient.request({ url: "https://example.com/", cache: cache });

b.httpClient.cache.create(opts) #

stable0.8.53
{
  store:                  ,    // Store: { get, set, delete, clear }
  sharedCache:            true,          // honour s-maxage; refuse Cache-Control: private
  defaultMaxStale:        0,             // seconds — serve stale up to this far past expiry
  revalidateInBackground: true,          // s-w-r kicks off background revalidation
  audit:                  undefined,     // audit sink with safeEmit({...})
  observability:          undefined,     // optional { event, safeEvent }
  statusHeader:           "x-blamejs-cache", // response header carrying the cache decision; null/false to suppress, or a custom name (e.g. "x-cache")
}

Builds an RFC 9111 cache instance for b.httpClient.request. The returned object plugs into a request via opts.cache. Without opts.cache, the request path is unchanged — no overhead for non-caching callers. The cache evaluates each response per RFC 9111 §3 (storage decision: method / status / Cache-Control / Vary), tracks freshness per §4.2 (s-maxage > max-age > Expires > heuristic 10% of (Date - Last-Modified) capped at 24h), revalidates conditionally per §4.3 (If-None-Match / If-Modified-Since), and merges 304 headers per §5.

sharedCache: true (default) honours s-maxage over max-age and refuses to store responses with Cache-Control: private — operator services share a cache with each other, so a per-user private response must not leak across users via the cache. Single-tenant scripts pass sharedCache: false to behave as a private cache.

defaultMaxStale lets the cache return a stored entry past its freshness lifetime (within the configured number of seconds) even without an explicit upstream stale-while-revalidate / stale-if-error. Default 0 — operators opt in.

revalidateInBackground (default true): when an entry is fresh within its stale-while-revalidate window the stale response is returned immediately and a background revalidation kicks off so the next caller sees a refreshed entry. Pass false to revalidate inline (lower memory churn, higher request latency).

var cache = b.httpClient.cache.create({
  store:           b.httpClient.cache.memoryStore({ maxBytes: 32 * 1024 * 1024 }),
  sharedCache:     true,
  defaultMaxStale: 5,
  audit:           b.audit,
});
var res = await b.httpClient.request({
  url:   "https://api.example.com/users/42",
  cache: cache,
});
// res.headers["x-blamejs-cache"] === "MISS" (first call)

Last updated 2026-08-08T16:39:49.652Z by seeder.