Cache
The cache is for values you can afford to lose and recompute: an expensive computation, a third-party API response. Each value has a time to live (TTL) and expires after it. For data that must last, use the KV store.
API
The framework provides createCache, imported from @bext-stack/framework/kv. It works over
any store that implements CacheStore (get, set, delete). The kvStore store keeps values
in your app's KV:
import { createCache, kvStore } from "@bext-stack/framework/kv";
// Shared across requests, scoped to your app, stored in KV with its TTL
const cache = createCache({ store: kvStore({ appId: "<app-id>" }), prefix: "rates" });
const rates = await cache.remember("eur", 300, () => fetchRates("EUR")); // TTL in seconds
await cache.put("flag", true, 60);
await cache.get<boolean>("flag"); // true | null
await cache.forget("flag");
Values are JSON-encoded, so you cache typed objects, not just strings. They must therefore be
JSON-serializable (a Date comes back as a string).
| Method | Description |
|---|---|
get<T>(key) |
Read a value, or null if it is absent or expired. |
put(key, value, ttlSecs?) |
Write a value. Without a TTL (or with 0), it does not expire. |
remember(key, ttlSecs, factory) |
Read the value, or compute it with factory, store it and return it. |
has(key) |
Whether a value is present. |
forget(key) |
Remove a value. |
pull<T>(key) |
Read then remove a value. |
add(key, value, ttlSecs?) |
Write only if the key is absent; returns true when written. |
Choosing a store
| Store | Scope |
|---|---|
kvStore({ appId }) |
Shared across requests, scoped to your app, stored in KV with its TTL. |
memoryStore() |
In memory, local to the instance running the code. Good for short-lived caching and tests. |
You can also supply your own store: an object with get(key), set(key, value, ttlSecs?) and
delete(key), sync or async. get must return exactly the string that set received, or null.
The prefix option prefixes every key (rates:eur), which keeps cache keys apart from your other
KV keys. remember treats only null as a miss: false, 0 and "" are cached like any other
value. A factory that returns null, however, runs again on every read.
Caching a loader
To cache a page loader's result, wrap it in cachedLoader. The cache is shared by all render
workers and keyed by host, route, route params and query string. Cookies and headers are not part
of the key, so only wrap loaders whose output does not depend on the visitor.
import { cachedLoader } from "@bext-stack/framework";
export const loader = cachedLoader(
{ ttl: 60, tags: ["projects"] }, // ttl in seconds, 60 when omitted
async ({ params }) => ({ project: await getProject(params!.slug) }),
);
| Option | Description |
|---|---|
ttl |
Time to live in seconds. Omitted or 0: 60 seconds. |
tags |
Tags used to invalidate entries with revalidateTag. |
vary |
A (args) => string function that adds key material, such as a locale. |
Only GET requests go through the cache. A null or undefined result or a Response is not
cached, and the result is stored as JSON.
revalidateTag("projects"), imported from @bext-stack/framework/cache, drops the entries
carrying that tag and returns the number of entries removed.
import { revalidateTag } from "@bext-stack/framework/cache";
revalidateTag("projects");
Cache or KV
Cache (createCache) |
KV (sdk.kv) |
|
|---|---|---|
| Use | Recomputable values | Durable data |
| Lifetime | Short, expires after the TTL | Kept until deleted (optional TTL) |
| Typical pattern | remember(key, ttl, factory) |
get / set / delete / list |
Use the cache to memoize an expensive computation or a response you can fetch again. Use KV when the data must survive: session-related state, job records, anything you cannot recompute.
Where to next
| Page | What it covers |
|---|---|
| KV Store | The durable string store |
| SDK Overview | The createSdk client |