Skip to content

Object Cache

EmDash reads content and site configuration from the database on every request. The object cache stores those query results in a fast key/value store, so repeat requests are served from the cache instead of the database. It reduces read load on the database — useful on Cloudflare, where KV serves far more requests per second than D1.

The object cache is optional and disabled by default. Enable it by adding an objectCache adapter to the emdash() integration.

BackendBest forShared across isolates
KVCloudflare WorkersYes
MemoryNode.js, local developmentNo (per process)

On Cloudflare, requests are served by many short-lived isolates across regions. KV is shared by all of them, so a value cached by one request is available to the next, anywhere. The memory backend caches within a single process, which suits a long-running Node.js server.

Configure the KV adapter and point it at a KV binding:

astro.config.mjs
import emdash from "emdash/astro";
import { d1, r2, kvCache } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
objectCache: kvCache({ binding: "CACHE" }),
}),
],
});

Create a KV namespace and add the binding to your Wrangler configuration.

Terminal window
npx wrangler kv namespace create CACHE

The command prints a namespace id. Add it under the binding name used in kvCache:

{
"kv_namespaces": [
{
"binding": "CACHE",
"id": "<namespace-id>"
}
]
}
OptionTypeDefaultDescription
bindingstringKV binding name from your Wrangler configuration. Required.
defaultTtlnumber3600Time-to-live for cached entries, in seconds. KV enforces a 60-second minimum.
revalidatenumber1000Isolate-local epoch-reuse window, in milliseconds. See Freshness.
timeoutnumber2000Maximum time, in milliseconds, to wait for a KV operation before treating it as a cache miss. Guards against a stalled KV read hanging the request. Set to 0 to disable.
keyPrefixstring"em"Prefix for every cache key. Set a unique value when several sites share one namespace.

The memory adapter caches within the server process. It needs no external service:

astro.config.mjs
import emdash, { memoryCache } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
objectCache: memoryCache(),
}),
],
});
OptionTypeDefaultDescription
defaultTtlnumber3600Time-to-live for cached entries, in seconds.
revalidatenumber1000Isolate-local epoch-reuse window, in ms.
maxEntriesnumber1000Maximum number of cached keys before older keys evict.
keyPrefixstring"em"Prefix for every cache key.

The object cache covers the reads that run on a typical page render:

  • Content queries: getEmDashCollection, getEmDashEntry, and resolveEmDashPath.
  • Site settings, navigation menus, and taxonomy terms.

Admin API requests, media files, and full HTML responses are not handled here. To cache rendered HTML at the edge, see Deploy to Cloudflare.

Editing content through the admin panel or the REST API invalidates the affected cache entries automatically. Creating, updating, publishing, or deleting an entry clears the cached queries for its collection; changing a byline or taxonomy term clears the entries that display it.

For anonymous visitors, a change takes time to appear across all isolates as they pick up the bumped epoch. With the in-isolate memory backend this is immediate. With Workers KV it is bounded by KV’s edge-cache propagation (eventual consistency, up to ~60 seconds) plus the isolate-local revalidate window (default one second). Lower revalidate for faster local propagation at the cost of more reads against the cache; raise it to read the cache less often.

Scheduled entries become visible when their publish time passes. A cached page reflects a newly-published scheduled entry on the next change to its collection, or when the cached entry’s defaultTtl lapses. If precise scheduled publishing matters for your site, set a lower defaultTtl.