Overview
Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation.
Voltro ships a first-class cache in @voltro/cache. It is always on — the in-process memory backend is the zero-config default, so ctx.cache works in every handler from the first boot with nothing to install. You opt into a distributed backend (Redis & friends) only when you need cross-instance sharing.
There are two ways to use it, and they share one backend:
- Low-level —
ctx.cache.wrap(key, { ttl, tags }, () => compute())in any handler. Cache-aside with tags, stale-while-revalidate, and single-flight de-duplication. Use it for expensive derived work: aggregations, external-API enrichment, rendered fragments. - Automatic query caching — add a
cachefield to adefineQueryand the framework caches the server snapshot and invalidates it automatically when a mutation writes any table the query depends on. Zero manual busting.
The defining idea mirrors the rest of Voltro: the backend is a configuration choice, the code is identical. The same wrap call and the same cache: field run against the in-process map in dev and against Redis (or Valkey / KeyDB / Dragonfly / Upstash) in production — you flip CACHE_BACKEND, not your handlers.
// app.config.ts — memory (default) or redis
export default { type: 'api', name: 'myApi', store: 'postgres', cache: 'redis' }// any handler — works on every backend
export default async (input: { orgId: string }, ctx) =>
ctx.cache.wrap(
`dashboard:${input.orgId}`,
{ ttlMs: 60_000, tags: [`org:${input.orgId}`] },
async () => expensiveDashboard(ctx, input.orgId),
)What it is not
- Not the reactive query engine. Live
useSubscriptionqueries already stay fresh by pushing deltas — caching is for non-live derived work and for sharing a query's initial snapshot across many subscribers/instances. - Not a runtime plugin. A cache backend is infrastructure, so it's an
app.config+ baseline concern (voltro add redis), not aplugins:entry.
Where to go next
- Backends & engines — memory vs Redis, the five RESP-compatible engines, connection presets, env vars.
- Low-level
wrap—ctx.cache/ the EffectCacheservice, tags, SWR, single-flight. - Query caching — the
cache:field, thescopesecurity rule, auto-invalidation. - Durable key-value —
ctx.kv(durable, never evicted — for state you can't recompute): the full API, TTL, tenant namespacing. - Key-value backends —
database(durable) vsredisvsmemory, theKV_BACKENDselector, theKvStoreport, custom backends. - Named connections — the shared registry behind the cache / KV / rate limiter / broadcast, the
<NAME>_REDIS_URL→REDIS_URLscheme, and per-concern enablement. - Enabling Redis —
voltro add redis,create-project --cache=redis, thevoltro cacheCLI, the dashboard panel.
The dashboard cache panel reports process-local counters since startup, even with a shared Redis backend. Missing cache instrumentation returns HTTP 503 instead of synthetic zero values. Read failures show the source error and hide prior measurements. No cache traffic is displayed separately from a measured zero hit rate.