Jerbix

Cache

Creating, binding, and using the Cloudflare KV cache

Last updated November 3, 2026


Server-side caching uses Cloudflare KV through the CACHE binding. The code is in src/cache/.

It fits read-heavy, write-light data where brief staleness is acceptable: third-party status lookups, public lists, slow endpoints. It does not fit data that needs strong consistency or frequent writes — KV writes propagate with a delay.

Steps

  1. Create a namespace.

``bash pnpm exec wrangler kv namespace create <your-namespace-name> ``

Copy the id from the output into wrangler.jsonc:

``jsonc title="wrangler.jsonc" { "kv_namespaces": [{ "binding": "CACHE", "id": "<your-namespace-id>" }] } ``

The binding is the name code reads (env.CACHE) and must match src/types/cloudflare.d.ts. The namespace name and the binding name are separate on purpose: the former identifies the resource in Cloudflare, the latter is the variable name in code.

  1. No extra switch. Caching is always on and there is no enabled option. Outside a request context (in tests, for example) every function degrades to pass-through: it calls the fetcher and neither caches nor throws.

Usage

ts
import { CACHE_TTL, createCacheKey, deleteCache, deleteCacheByPrefix, getOrSetCache } from '@/cache'

// Read: a hit returns immediately, a miss calls the fetcher, writes KV, and returns
const status = await getOrSetCache({
  key: createCacheKey('newsletter:status', email),
  ttlSeconds: CACHE_TTL.newsletterStatus,
  fetcher: async () => fetchStatusFromProvider(email),
})

// Invalidate
await deleteCache(createCacheKey('newsletter:status', email))   // single key
await deleteCacheByPrefix(createCacheKey('products', 'list'))    // by prefix

Always build keys with `createCacheKey()`: it replaces : inside each part with _ and joins the parts with :, so a colon in an argument cannot pollute the key levels and cause a prefix invalidation to delete the wrong entries. Use the TTL constants from src/cache/constants.ts instead of writing numbers at the call site:

ConstantSecondsUsed for
CACHE_TTL.newsletterStatus3600Subscription status
CACHE_TTL.priceCatalog60Payment price catalog, so a price change shows up within a minute
CACHE_TTL.providerStatus300Third-party provider status
CACHE_TTL.publicList60Public lists

Only newsletterStatus and priceCatalog have callers today; the other two are ready to use. Invalidate the affected key right after a write instead of waiting for the TTL to expire.

Current consumers

LocationPurpose
src/newsletter/index.tsCaches subscription status, invalidated after subscribe and unsubscribe
src/lib/rate-limit.tsBuilds rate limit keys with createCacheKey
src/payment/price-catalog.server.tsCaches the payment price catalog

Rate limiting and caching share the same CACHE namespace.

Common commands

CommandWhat it does
pnpm exec wrangler kv namespace create <name>Create a namespace
pnpm exec wrangler kv namespace listList namespaces
pnpm exec wrangler kv key list --namespace-id <id>List keys

Once this is wired up you can move on to File Storage.