deco-edge-caching

Complete caching infrastructure for Deco storefronts on Cloudflare Workers, provided by @decocms/start/sdk .

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "deco-edge-caching" with this command: npx skills add decocms/deco-start/decocms-deco-start-deco-edge-caching

Deco Edge Caching

Complete caching infrastructure for Deco storefronts on Cloudflare Workers, provided by @decocms/start/sdk .

Architecture Overview

Three caching layers work together:

Layer What Where TTL Control

Edge cache Full HTML responses Cloudflare Cache API + CDN cacheHeaders(profile) via worker-entry

Loader cache VTEX/Shopify API data In-memory per-isolate (V8) createCachedLoader() in setup.ts

Client cache Route data after navigation TanStack Router memory routeCacheDefaults(profile) on routes

The worker-entry is the authority for edge caching. Routes declare intent via headers() and staleTime , but the worker-entry overrides Cache-Control based on URL-detected profiles.

Cache Profiles

Profile Browser max-age

Edge s-maxage

SWR Client staleTime

static

1 hour 1 day 1 day 5 min

product

60s 5 min 1 hour 1 min

listing

30s 2 min 10 min 1 min

search

0 60s 5 min 30s

cart

private — — 0

private

private — — 0

none

private — — 0

URL-to-Profile Detection (built-in)

URL Pattern Detected Profile

/

static

*/p (ends with /p) product

/s , /s/* , ?q=

search

/cart , /checkout , /account , /login

private

/api/* , /deco/* , /_server , /_build

none

Everything else listing (conservative default)

Site Worker Entry (10 lines)

CRITICAL: wrangler.jsonc must point to this custom file, NOT to @tanstack/react-start/server-entry . TanStack Start's Cloudflare adapter completely ignores custom export default — it generates its own handler that calls createStartHandler directly. Without a separate worker-entry, none of the caching, admin routes, or purge logic will execute in production.

// wrangler.jsonc { "main": "./src/worker-entry.ts" // NOT: "main": "@tanstack/react-start/server-entry" }

// src/worker-entry.ts import "./setup"; import handler, { createServerEntry } from "@tanstack/react-start/server-entry"; import { createDecoWorkerEntry } from "@decocms/start/sdk/workerEntry";

const serverEntry = createServerEntry({ async fetch(request) { return await handler.fetch(request); }, });

export default createDecoWorkerEntry(serverEntry);

With Custom Overrides

export default createDecoWorkerEntry(serverEntry, { // Override profile for specific URLs detectProfile: (url) => { if (url.pathname.startsWith("/institucional")) return "static"; return null; // fall through to built-in detection }, // Disable mobile/desktop cache key splitting deviceSpecificKeys: false, // Custom purge token env var purgeTokenEnv: "MY_PURGE_TOKEN", // Add extra bypass paths extraBypassPaths: ["/preview/"], });

Route Setup

import { createFileRoute } from "@tanstack/react-router"; import { cacheHeaders, routeCacheDefaults } from "@decocms/start/sdk/cacheHeaders";

export const Route = createFileRoute("/")({ ...routeCacheDefaults("static"), headers: () => cacheHeaders("static"), loader: () => loadPage(), component: HomePage, });

For the CMS catch-all route:

export const Route = createFileRoute("/$")({ ...routeCacheDefaults("listing"), headers: () => cacheHeaders("listing"), loader: async ({ params }) => { /* ... */ }, component: CmsPage, });

The worker-entry overrides the route's Cache-Control with the correct profile for each URL (PDP gets product , search gets search , etc.), so the route's headers() serves as a fallback for client-side navigation responses via /_server .

Registering Custom Patterns

// In setup.ts or worker-entry.ts import { registerCachePattern } from "@decocms/start/sdk/cacheHeaders";

registerCachePattern({ test: (pathname) => pathname.startsWith("/blog"), profile: "static", });

Custom patterns evaluate before built-in ones.

Factory Features

createDecoWorkerEntry provides:

  • Cache API integration — caches.default.match() / .put() for edge caching

  • Device-specific keys — mobile/desktop get separate cached HTML (__cf_device param)

  • Per-URL profiles — detectCacheProfile(url) selects the right Cache-Control

  • Immutable static assets — /_build/assets/-{hash}. get immutable, max-age=31536000

  • Private path protection — strips public Cache-Control from cart/checkout/account responses

  • Cache API TTL fix — stores with max-age={sMaxAge} since Cache API ignores s-maxage

  • Purge API — POST /_cache/purge with bearer token to invalidate paths

  • Diagnostic headers — X-Cache: HIT|MISS and X-Cache-Profile: {profile}

Debugging Cache

Check profile and cache status

curl -s -D - -o /dev/null "https://site.com/category-slug" | grep -iE "cache-control|x-cache"

Expected first hit:

cache-control: public, max-age=30, s-maxage=120, stale-while-revalidate=600

x-cache: MISS

x-cache-profile: listing

Expected second hit:

cf-cache-status: HIT

x-cache: HIT

age: 3

Purge cache

curl -X POST "https://site.com/_cache/purge"
-H "Authorization: Bearer $PURGE_TOKEN"
-H "Content-Type: application/json"
-d '{"paths": ["/", "/vedacao-externa"]}'

Important: Use GET requests for testing (not curl -I which sends HEAD). The worker-entry only caches GET requests.

Loader Cache (Server-Side SWR)

In setup.ts , wrap commerce loaders with createCachedLoader :

import { createCachedLoader } from "@decocms/start/sdk/cachedLoader";

const cachedPLP = createCachedLoader("vtex/plp", vtexPLP, { policy: "stale-while-revalidate", maxAge: 60_000, });

This is per-isolate in-memory cache (V8 Map). Resets on cold start. Includes request deduplication (single-flight) and LRU eviction at 500 entries.

Key Constraints

  • Cache API ignores s-maxage — the factory uses max-age equal to sMaxAge when storing in Cache API

  • In-memory loader cache is ephemeral — resets when Workers isolates recycle (~30s idle)

  • Device keys add a query param — __cf_device=mobile|desktop is appended to cache keys, so purging must clear both

  • Non-200 responses are never cached — only 200 OK goes into Cache API

  • /_server paths always bypass cache — TanStack Start RPC requests are never edge-cached

Package Exports

// Headers and profiles import { cacheHeaders, routeCacheDefaults, detectCacheProfile } from "@decocms/start/sdk/cacheHeaders"; import { getCacheProfileConfig, registerCachePattern } from "@decocms/start/sdk/cacheHeaders";

// Worker entry factory import { createDecoWorkerEntry } from "@decocms/start/sdk/workerEntry";

// Loader cache import { createCachedLoader, clearLoaderCache } from "@decocms/start/sdk/cachedLoader";

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

deco-storefront-test-checklist

No summary provided by upstream source.

Repository SourceNeeds Review
General

deco-tanstack-search

No summary provided by upstream source.

Repository SourceNeeds Review
General

deco-site-memory-debugging

No summary provided by upstream source.

Repository SourceNeeds Review
General

deco-e2e-testing

No summary provided by upstream source.

Repository SourceNeeds Review