Skip to main content
Use @unkey/cache (version ) to add a typed cache to your own services, with several layers such as memory in front of Redis. It doesn’t call the Unkey API and needs no root key. You define typed namespaces, give each a list of stores, and get get, set, remove, and swr with fresh and stale times handled for you.

Install

Quickstart

Reads and writes return { val, err } instead of throwing, so a failing store acts like a cache miss instead of breaking your request.

Concepts

Namespaces

A namespace holds one type: Namespace<User> only accepts and returns User. createCache returns an object with one entry per namespace, each with get(key), set(key, value, opts?), remove(key) (which also takes an array), and swr(key, loadFromOrigin).

Tiered stores

stores is a list in order. Reads try each store and return the first hit, copying it back to the earlier stores. Writes go to every store. A common setup is memory first, then a shared store such as Cloudflare or Redis.

Fresh and stale

fresh and stale are in milliseconds. Before fresh, a value is returned as is. Between fresh and stale, it’s still returned, and swr reloads it in the background through ctx.waitUntil (that’s why a context is required). After stale, it counts as a miss.

Stale-while-revalidate

swr returns the cached value if there is one, refreshes it in the background if it’s past fresh, and only waits for loadFromOrigin on a miss. Several misses for the same key at once load the origin only once.

Stores and middlewares

The @unkey/cache/stores entry point exports MemoryStore, CloudflareStore (the Cloudflare Cache API, configured with an API key, zone ID, and domain), UpstashRedisStore, and LibSQLStore for Turso and other libSQL databases. Any object implementing the exported Store interface (name, get, set, remove) can be used as a tier. @unkey/cache/middleware exports withEncryption(base64Key), which encrypts values before they reach a store, and withMetrics(metrics), which reports hits, misses, and latency. Each returns an object with a wrap(store) method. Put the wrapped store in stores, not the middleware. withEncryption is async and takes 32 random bytes in base64 (openssl rand -base64 32). Changing the key makes every entry it wrote unreadable, so they become misses.

Source

The package is in the unkeyed/sdks repository. src/examples/ has runnable samples.
Last modified on September 29, 2026