Skip to content

Sync Endpoint

The /sync endpoint populates Cloudflare KV with catalog data from the Maho backend. It's called by the admin module (Mageaustralia_Storefront) or via cron to keep the storefront's data current.

Full Sync

Syncs all entity types for all stores.

bash
curl -X POST https://your-store.com/sync \
  -H "Content-Type: application/json" \
  -d '{"secret": "your-sync-secret"}'

Response:

json
{
  "success": true,
  "synced": {
    "config": 1,
    "categories": 42,
    "products": 1523,
    "cmsPages": 8,
    "blogPosts": 12,
    "countries": 1
  },
  "duration": "4.2s"
}

Partial Sync

Sync a specific entity type only.

EndpointEntities
POST /sync/configStore config (name, currency, locale, etc.)
POST /sync/categoriesAll active categories with product counts
POST /sync/productsAll products (paginated, all pages)
POST /sync/cms-pagesAll CMS pages
POST /sync/blog-postsAll blog posts
POST /sync/countriesCountry list with regions
bash
# Sync only products
curl -X POST https://your-store.com/sync/products \
  -H "Content-Type: application/json" \
  -d '{"secret": "your-sync-secret"}'

Sync Process

KV Key Patterns

During sync, data is written to KV with store-prefixed keys:

KeyContent
{store}:configStoreConfig JSON
{store}:categoriesCategory tree JSON array
{store}:product:{urlKey}Individual product JSON
{store}:products:category:{id}:page:{n}Category product listing page
{store}:cms:{identifier}CMS page content
{store}:blog:{identifier}Blog post content
{store}:countriesCountry list with regions
pulseData version hash (triggers edge cache bust)

Pulse Hash

After every sync, the pulse KV key is updated with a new hash. This hash is combined with ASSET_HASH to form the edge cache version tag - any change in pulse automatically invalidates all edge-cached HTML pages.

Product Sync Details

Products are fetched in pages of 100 via auto-pagination:

  1. MahoApiClient.fetchAllPages('/products?itemsPerPage=100')
  2. Each product is stored individually: {store}:product:{urlKey}
  3. Products are also grouped by category for listing pages

For large catalogs (1000+ products), full sync can take several seconds. Use partial syncs for targeted updates.

Listing-stub refetch

The category-listing API response drops type-specific child arrays — a grouped/bundle/downloadable/giftcard product's KV entry only carries its listing-view fields, not the children a PDP needs to render its options.

src/routes/url-resolver.tsx detects this at request time and pulls /api/rest/v2/products/{id} (full detail) before rendering. The stub check covers each type that has a child array:

TypeMissing when
configurableconfigurableOptions empty
groupedgroupedProducts empty
bundlebundleOptions empty
downloadabledownloadableLinks empty
giftcardgiftcardType unset

On a hit, the refetched full record is written back to {store}:product:{urlKey} with a 24-hour TTL, so subsequent visits use it directly. Adding a new product type with child arrays? Extend the isListingStub union in the URL resolver — see creating a variant → Step 5.

Admin Module Integration

The Maho admin module (Mageaustralia_Storefront) triggers syncs automatically:

  • On product save → partial product sync
  • On category save → partial category sync
  • On CMS page save → partial CMS sync
  • Cron job → full sync on schedule
  • Manual → "Sync Now" button in admin

Source: src/index.tsx, src/api-client.ts