Skip to content

cms-cf-worker

A Cloudflare Worker that renders the public website at the edge from the CMS public v2 API. There are no per-page HTML files on a server: every response is built on demand by a single, generic, data-driven engine and cached close to the visitor.

Source: src/index.ts, wrangler.toml, package.json.

One codebase, two workers

The repository ships two Workers from the same source tree, selected by the Wrangler environment that is deployed:

Worker Entry point Role
Site worker src/index.ts Renders pages, serves stylesheets and static text files, handles cache purge. Backed by a KV namespace.
Media worker src/media/index.ts Streams media objects from S3 at the /cmp-media/* routes. Stateless; uses the edge Cache API only, no KV.

This documentation is about the site worker (src/index.ts). The media worker is mentioned only where it shares routes or deploy mechanics (see Deploy and purge).

What the worker does

The worker is the public front door for a CMS-managed site (for example wantskicks.com). For each request it:

  1. fetches the site's settings and page descriptors from the CMS public v2 API (cached aggressively at several layers);
  2. matches the request path to a page descriptor and renders HTML, either through a named builder or the generic declarative path;
  3. caches the rendered page and serves subsequent hits straight from the edge.

There is no hardcoded per-page code on the request path. Adding a new simple page type is configuration in the CMS, not a worker deploy. See Architecture.

Request lifecycle at a glance

handleRequest in src/index.ts walks a fixed sequence of steps. The numbered comments in the source map one-to-one onto the stages below.

flowchart TD
    A[Request] --> B{Admin endpoint?<br>/__cms/purge, /__cms/health}
    B -- yes --> Z[Handle and stamp, bypass caches]
    B -- no --> C{CSS asset path?<br>/_assets/styles/...}
    C -- yes --> D[serveTemplateCss, fall back to bundled CSS]
    C -- no --> E{Preview?<br>valid ?_preview= secret}
    E -- no --> F{Edge cache hit?<br>Cache API}
    F -- hit --> G[Return cached, X-CMS-Cache: hit]
    F -- miss --> H[resolveSite: mem to Cache API to origin to KV]
    E -- yes --> H
    H --> I[Apply CSS override if env set]
    I --> J{Static text?<br>ads.txt, robots.txt, favicon.ico}
    J -- yes --> K[Serve from settings]
    J -- no --> L[matchDescriptor, else notFoundDescriptor]
    L --> M{Preview?}
    M -- yes --> N[Render fresh, never read or write cache]
    M -- no --> O[getOrRender: Cache API, render, KV fallback]

The exact steps, keyed to the src/index.ts comments:

  • Step 1 -- admin endpoints. /__cms/purge and /__cms/health take absolute priority and skip every cache layer. See Deploy and purge.
  • Step 1b -- per-template stylesheet. A request for /_assets/styles/<slug>/<version>.css is served by serveTemplateCss (src/assets/serve.ts); on any miss it falls back to the bundled stylesheet. See CSS override.
  • Step 1c -- bundled stylesheet. The legacy single-file path /_assets/styles/site.css is served directly from the worker bundle.
  • Step 2 -- preview markers. ?_preview=<ADMIN_PURGE_SECRET> plus an optional ?_ver= enables a fresh, cache-bypassing render for editors.
  • Step 3 -- cache-first short-circuit. For non-preview requests the worker checks the Cache API first and returns the cached body immediately on a hit. See Caching.
  • Step 4 -- resolve site. resolveSite (src/site/resolve.ts) loads the full site settings through its own three-tier cache (isolate memory, Cache API, origin) with KV as a fallback.
  • CSS override. When CSS_OVERRIDE_SLUG and CSS_OVERRIDE_VERSION are set on the worker, the resolved settings' active_template is cosmetically overridden in memory. See CSS override.
  • Step 5 -- static text files. /ads.txt, /robots.txt, and /favicon.ico are served from the cached settings.
  • Step 6 -- route and dispatch. matchDescriptor (src/engine/router.ts) picks the best descriptor for the path; an unmatched path falls back to notFoundDescriptor (src/engine/settings.ts). See Routing.
  • Step 7 -- render and cache. For public requests getOrRender (src/cache/index.ts) returns the cached page, or renders fresh and write-throughs to both the Cache API and KV. Preview requests render fresh and never touch the cache.

Every response carries observability headers X-CMS-Cache (which layer served it) and X-CMS-Render-Ms (wall-clock time in the worker). See Caching for the full set of header values.

Source tree map

Path Responsibility
src/index.ts Site worker entry point and request lifecycle.
src/engine/router.ts matchDescriptor / matchPattern -- path to descriptor.
src/engine/render.ts renderPage -- dispatch a matched descriptor to a builder.
src/engine/generic.ts renderGeneric -- the code-free declarative builder.
src/engine/settings.ts DEFAULT_PAGE_TYPES, getPageTypes, notFoundDescriptor.
src/engine/sources.ts Executes a descriptor's declared upstream API calls.
src/site/resolve.ts resolveSite -- the site-settings cache tiers.
src/cache/index.ts getOrRender, purge, purgeSite -- page cache and purge.
src/cms/client.ts cmsFetch -- authenticated GET against the CMS public v2 API.
src/assets/serve.ts serveTemplateCss / parseTemplateCssPath -- template CSS.
src/layout/index.ts renderLayout, buildStyle, the Handlebars-like renderTemplate.
src/admin/purge.ts POST /__cms/purge handler.
src/pages/*.ts The named builders (homepage, article, category, author, ...).
src/media/index.ts The separate media worker.
wrangler.toml Worker names, routes, env vars, KV bindings for every env.

Where to go next