Cloudflare KV
Use Cloudflare KV for the MainSDK cache and runtime settings - namespaces, main.yaml, credentials, eventual consistency, testing and swapping.
Cloudflare KV is a key-value store replicated across Cloudflare's network, and a driver behind two interfaces: kv, for small values and cached results, and config, for typed runtime settings that change without a deploy. Reads are fast everywhere because values are cached close to the reader. Pick it when your app already runs on Cloudflare, or when you want settings and cached values read quickly in many places and can accept that a write takes a short while to show up everywhere.
Interfaces it serves
Pick Cloudflare KV in main.yaml for any of these. Your calls stay the same, and you can swap later.
kvBuilding for 0.3Driver cloudflare-kv Cloudflare KV
planned
kv:
driver: cloudflare-kvSwap to another provider
configBuilding for 0.2Driver cloudflare-kv Cloudflare KV
planned
config:
driver: cloudflare-kvSwap to another provider
Before you start
You need a Cloudflare account.
- Create a KV namespace for each use. Keep cached values and runtime settings in separate namespaces, and use separate namespaces for development and production.
- Create an API token scoped to the account's Workers KV storage with the least rights the driver needs: read for
config, read and write forkv. Copy the token and your account ID. - Note each namespace ID. The driver needs it to find your values.
Store the token in your secrets manager, never in code or a committed env file.
Configure
For runtime settings:
config:
driver: cloudflare-kv
schema: ./config.schema.json
# token: secret:CLOUDFLARE_API_TOKEN # suggested name; connection keys (account, namespace) are [to be decided]For the cache and key-value store:
kv:
driver: cloudflare-kvThe config spec leaves the connection keys for each hosted store (store id, region, namespace) undecided, and kv connection settings arrive with its spec. Settings are not secrets: keep credentials in Secrets.
Use it
Read a typed setting and react when it changes:
import { config, log } from "mainsdk";
export function canAdd(count: number) {
return count < config.get("cart.maxItems"); // typed from config.schema.json
}
const stop = config.watch("cart.maxItems", (v) => log.info("changed", { v }));Store a value with an expiry:
"use server";
import { auth, kv } from "mainsdk";
export async function saveDraftStep(req: Request, step: number) {
const session = await auth.require(req);
await kv.set(`onboarding:${session.user.id}`, { step }, { ttl: "1d" });
}Local development and testing
- No account: use
config: { driver: files }inmain.env.dev.yaml, and the in-memory fakes in tests. The local driver forkvis not named yet. - A development namespace: point your development overlay at its own namespace to try the real store.
- Tests:
createTestApp()from@mainsdk/testingrunsconfigandkvon in-memory fakes.chaos.on("config", { outage: true })provesconfig.get()keeps serving the last good value when Cloudflare is unreachable;chaos.on("kv", { outage: true })does the same drill for the cache.
What MainSDK adds on top
- Settings typed against your schema; a bad value is rejected with
MAIN-CONFIG-0020and the last good one is kept. - Every settings change the app sees is audited, with the key, hashes of old and new values and the source.
kvkeys scoped per tenant, a TTL on every entry, and no personal data in shared caches without a label.- The same calls on every driver, so moving settings or cache off Cloudflare is one line.
See Runtime settings and Key-value store and cache.
Limits and notes
- Cloudflare KV is eventually consistent: a write can take a while to reach every location. Don't use it for counters, locks or anything that needs read-after-write; use
limitsanddbinstead. cloudflare-kvcomes with the config-secrets spec forconfig(phase 1). Forkvit is a planned phase 1 driver, andkvhas no spec yet.- Cloudflare replicates KV values across its network, so keep personal data out of it unless your residency rules allow that copy. MainSDK regions are US, EU and India; see Data and regions.
- Running your app itself on Cloudflare Workers comes with the portable TypeScript core (spec portable-core, 0.4). How the driver reaches KV from Node is not decided yet.
config.raw()returns the provider's client for featuresconfigdoes not wrap; every use is logged and listed bymainsdk check.
Swap to or from it
config:
driver: cloudflare-kv # was vercel-global-configYour schema, config.get() and config.watch() calls stay the same; copy the values into the new namespace first. For kv, cached entries refill on the new driver as they expire.
FAQ
Why didn't my change show up straight away? KV is eventually consistent. Expect a short delay before every reader sees a new value; config.watch() fires when your app sees it.
Should settings and cache share a namespace? No. Keep them apart so a cache write can never overwrite a setting, and so the config token can be read-only.
Next
Open questions
Was this page helpful?