Files

Workers Cache demo

vinext ships two Cloudflare cache adapters you declare in vite.config.ts:

  • cdnAdapter() serves page-level ISR through the Cloudflare Workers Cache (ctx.cache). The origin renders fresh and the edge absorbs HIT/STALE traffic, revalidating in the background; revalidateTag() / revalidatePath() fan out to ctx.cache.purge({ tags }).
  • kvDataAdapter() backs the inner "use cache" / fetch data cache with a Workers KV namespace instead of in-memory.

Both are wired up in vite.config.ts:

import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";

vinext({
  cache: {
    cdn: cdnAdapter(),
    data: kvDataAdapter(), // binding defaults to VINEXT_KV_CACHE
  },
});

cdnAdapter() generates the per-entrypoint Workers Cache configuration during the Cloudflare build. The KV adapter still needs a matching VINEXT_KV_CACHE namespace binding in wrangler.jsonc.

The adapter adds a transport-only URL digest so distinct response-stage identities cannot collide. Workers Cache owns this key independently of zone Cache Rules.

What's in the box

  • ISR-cached App Router page at /cached/[slug] (revalidate = 60).
  • Cached App Route handler at /api/now (revalidate = 30).
  • Force-dynamic comparison page at /dynamic.
  • Revalidation API at /api/revalidate-tag and /api/revalidate-path that drives the UI's "Invalidate this page" controls.
  • A client-side probe that issues a no-store fetch against a route and prints the headers Cloudflare's edge attaches — cf-cache-status (the outer Workers Cache verdict), Age, plus the Cache-Control / Cache-Tag headers vinext emits to drive it.

How vinext wires it up

  1. vite.config.ts declares the adapters via vinext({ cache }) (see above). The declarations do not touch the Workers runtime during config evaluation. The Cloudflare build hook emits the staged Worker configuration, and runtime adapters instantiate lazily on the first request.

  2. wrangler.jsonc binds the KV namespace:

    {
      "kv_namespaces": [{ "binding": "VINEXT_KV_CACHE", "id": "<your-kv-namespace-id>" }],
    }
    

    Create the namespace with npx wrangler kv namespace create VINEXT_KV_CACHE and drop the returned id in.

  3. The Worker entry uses vinext's router-selected handler directly from wrangler.jsonc:

    {
      "main": "vinext/server/fetch-handler",
    }
    

    When cdnAdapter() is configured, vinext asks the Cloudflare build for two Worker entrypoints. The default entrypoint runs routing and middleware with caching disabled; VinextCachedResponse lazily loads the render stage with Workers Cache enabled. The generated dist/server/wrangler.json contains those settings, so existing applications do not need a source-config edit. The handler also passes the Worker's env so kvDataAdapter() can resolve its KV binding. No manual registration.

  4. ISR responses carry Cloudflare-CDN-Cache-Control: public, max-age=N, stale-while-revalidate=M (for the edge) plus a browser-facing Cache-Control: public, max-age=0, must-revalidate, and a Cache-Tag header listing both the bare path (/cached/intro) and Next.js's internal _N_T_<path> form. The Workers Cache reads these to cache and tag-purge.

  5. revalidateTag / revalidatePath in your route handlers fan out to both the KV data cache and ctx.cache.purge(...) on the platform layer.

Running locally

pnpm install
pnpm dev

Then open http://localhost:5173 and click into any of the demo routes.

Note: dev runs on @cloudflare/vite-plugin (miniflare), so the VINEXT_KV_CACHE namespace is emulated locally and kvDataAdapter() works. The edge CDN layer (cf-cache-status, background revalidation) only runs on Cloudflare's edge — locally the Cloudflare-CDN-Cache-Control / Cache-Tag headers cdnAdapter() emits are what drive it once deployed.

Deploying

pnpm build
npx wrangler deploy