Recipes
Recipes
Idiomatic compositions across @mongez/cache features and across the Mongez family.
Bootstrap once at app entry
import { PlainLocalStorageDriver, setCacheConfigurations,} from "@mongez/cache";
setCacheConfigurations({ driver: new PlainLocalStorageDriver(), prefix: `${import.meta.env.VITE_APP_KEY}-`, expiresAfter: 60 * 60 * 24, // 1 day default});Then everywhere else:
import cache from "@mongez/cache";
cache.set("user", payload);cache.get("user");Multi-app namespacing on the same domain
// in app AsetCacheConfigurations({ driver: new PlainLocalStorageDriver(), prefix: "app-a-",});
// in app BsetCacheConfigurations({ driver: new PlainLocalStorageDriver(), prefix: "app-b-",});Each app sees its own user, token, prefs.theme, etc. without leaking into the other.
TTL — keeping a short cache for derived data
async function getProductRecommendations(productId: string) { const cached = cache.get(`recs.${productId}`); if (cached) return cached;
const recs = await api.recommendations(productId); cache.set(`recs.${productId}`, recs, 60 * 15); // 15 minutes return recs;}The recommendations are recomputed at most every 15 minutes per product, surviving reloads but not stale forever.
Encrypted tokens
import { encrypt, decrypt, setEncryptionConfigurations } from "@mongez/encryption";import { EncryptedLocalStorageDriver, setCacheConfigurations,} from "@mongez/cache";
setEncryptionConfigurations({ key: import.meta.env.VITE_APP_SECRET });
setCacheConfigurations({ driver: new EncryptedLocalStorageDriver(), encryption: { encrypt, decrypt },});
cache.set("auth.accessToken", token);cache.set("auth.refreshToken", refreshToken);On disk the values are AES cyphers, not plaintext. Readable only with the configured key.
Sibling stores — long-lived prefs + ephemeral session
import { CacheManager, PlainLocalStorageDriver, PlainSessionStorageDriver,} from "@mongez/cache";
export const prefs = new CacheManager();prefs .setDriver(new PlainLocalStorageDriver()) .setPrefixKey("pref-");
export const session = new CacheManager();session .setDriver(new PlainSessionStorageDriver()) .setPrefixKey("session-");
prefs.set("theme", "dark"); // localStoragesession.set("scroll.y", 312); // sessionStorageTwo managers, two prefixes, two backends — explicit at every call site.
SSR fallback to runtime driver
import { PlainLocalStorageDriver, RunTimeDriver, setCacheConfigurations,} from "@mongez/cache";
const driver = typeof window === "undefined" ? new RunTimeDriver() : new PlainLocalStorageDriver();
setCacheConfigurations({ driver });Same call sites work on server and client. The server sees an empty in-memory cache (fresh per request if you re-bootstrap per request) and the client takes over with persistent storage on hydration.
Persisting an atom via @mongez/cache
@mongez/atom’s persist slot accepts any PersistAdapter<V>. The cache’s API matches by name; a thin wrapper normalizes return values:
import cache from "@mongez/cache";
export const cacheAdapter = { get: (key: string) => cache.get(key), set: (key: string, value: unknown) => { cache.set(key, value); }, remove: (key: string) => { cache.remove(key); },};Then:
import { createAtom } from "@mongez/atom";import { cacheAdapter } from "./adapters/cacheAdapter";
const themeAtom = createAtom({ key: "ui.theme", default: "light", persist: cacheAdapter,});
const userAtom = createAtom({ key: "auth.user", default: { name: "Anon" }, persist: cacheAdapter,});Every themeAtom.update("dark") writes through to the configured backend; every page load reads back from it.
The wrapper exists so:
setandremovereturnvoidinstead of the driver instance. Atom doesn’t care about the chain.- The wrapper sits in one place — if you swap from localStorage to encrypted localStorage to IndexedDB, every atom upgrades at once.
Per-atom backend (mixing persistence strategies)
If different atoms want different backends, build one adapter per backend:
import { CacheManager, PlainLocalStorageDriver, EncryptedLocalStorageDriver,} from "@mongez/cache";
const plain = new CacheManager();plain.setDriver(new PlainLocalStorageDriver()).setPrefixKey("app-");
const encrypted = new CacheManager();encrypted.setDriver(new EncryptedLocalStorageDriver()).setPrefixKey("secure-");
export const plainAdapter = { get: (k: string) => plain.get(k), set: (k: string, v: unknown) => { plain.set(k, v); }, remove: (k: string) => { plain.remove(k); },};
export const encryptedAdapter = { get: (k: string) => encrypted.get(k), set: (k: string, v: unknown) => { encrypted.set(k, v); }, remove: (k: string) => { encrypted.remove(k); },};
// atoms/preferences.tsconst themeAtom = createAtom({ key: "ui.theme", default: "light", persist: plainAdapter,});
// atoms/auth.tsconst tokenAtom = createAtom({ key: "auth.token", default: "", persist: encryptedAdapter,});The token sits encrypted on disk; the theme sits plain. The atom code doesn’t know the difference.
Subscribe to cache writes (custom)
@mongez/cache doesn’t emit events. If you need write-through subscriptions, wrap the cache:
import events from "@mongez/events";import cache from "@mongez/cache";
export const observableCache = { set(key: string, value: unknown, expiresAfter?: number) { cache.set(key, value, expiresAfter); events.trigger("cache.set", { key, value }); }, get: cache.get.bind(cache), remove(key: string) { cache.remove(key); events.trigger("cache.remove", { key }); }, on: events.on.bind(events),};
observableCache.on("cache.set", ({ key, value }) => { console.log("wrote", key, value);});Or — usually simpler — route the same data through a @mongez/atom atom with a persist adapter and subscribe to the atom instead.