Cache
Reads are the common case. Keep them off the database without giving up durability — L1 is memory, L2 is SQLite, and neither needs a Redis instance.
Setup
import { createCache, SQLiteL2CacheStore } from "yatta/cache"; export const cache = createCache({ maxItems: 20_000, // L1 capacity defaultTtl: "1h", l2Storage: new SQLiteL2CacheStore("Database/cache.db"),});Drop l2Storage for a purely in-memory cache — useful in tests, or when a restart should reset everything.
Reading and writing
await cache.set("user:42", { name: "Ada" }, { ttl: "5m" }); const user = await cache.get<{ name: string }>("user:42");// → { name: "Ada" } | null await cache.delete("user:42");remember
remember is the useful one. It caches a factory's result, and while a value is being computed only one call runs — the rest wait for it. That removes the thundering herd when a popular key expires.
const user = await cache.remember( `user:${id}`, "5m", // fresh for 5 minutes () => fetchUserFromDatabase(id), { swr: "30m", // serve stale while refreshing tags: ["users", `user:${id}`], },);swr set, an expired entry is still returned while a background refresh runs. Requests never wait on the refresh.Tag invalidation
await cache.set(`user:42`, data, { tags: ["users", "user:42"] });await cache.set(`user:43`, data, { tags: ["users", "user:43"] }); // Drops every key tagged "users" across L1 and L2await cache.invalidateTags(["users"]);Tag the keys you can name and invalidate the group — you do not need to remember each individual key.
Consistency
L2 writes default to awaiting the disk, which is slower but means a read after a write always sees it. Set consistency: "async" to let writes land in the background.
await cache.set("k", v, { consistency: "async" });Inspecting
const stats = cache.getMetrics();// { size, hits, misses, evictions, hitRate, … }Durations
Anywhere a duration is accepted you can pass a number of milliseconds or a readable string.
"500ms" "10s" "5m" "2h" "7d" "1w" // strings500 // raw millisecondsGlobal instance
If you do not want to thread a cache instance through your code, a configured default is available as a named export.
import { Cache } from "yatta/cache"; await Cache.set("k", v);const v = await Cache.get("k");get returns unknown when no type parameter is given. Pass one — cache.get<User>("u:1") — so the result is usable.