Appearance
Overview
SaveLayer is designed around one shared service layer with multiple ingress channels.
Online Store themes
Online Store traffic uses Shopify app proxy requests. The recommended storefront integration is the SaveLayer theme app embed, which loads the SDK and exposes window.SaveLayer. Full endpoint and schema reference lives on the SaveLayer marketing documentation site under Documentation → API reference (/docs/api-reference).
Headless storefronts
Headless storefronts use direct APIs under /api/headless/*: a backend-only exchange (POST /api/headless/auth/exchange) with a Customer Account API access token, then short-lived SaveLayer JWTs (Authorization: Bearer …, 300s TTL) on operation routes. They do not go through the Shopify app proxy path.
Customer account extensions
Customer account extensions use direct APIs under /api/customer-account/*: a session-token exchange (POST /api/customer-account/auth/exchange), then the same SaveLayer JWT model on operations. Exchange responses use Shopify’s CORS helper; other routes use the same customer-account authenticate path for OPTIONS preflight.
Full diagrams, secret setup, and security boundaries: SaveLayer marketing documentation Documentation → Authorization (/docs/authorization).
How saved state is stored
Saved state is held in Cloudflare D1 (saved_state) as the source of truth. Every save/remove goes through the shared service layer, which upserts D1 and enqueues a projection job. The savelayer-saved-sync worker consumer then projects D1 into a single Shopify save_list metaobject per (customer, list, access). Saved entities live inline on that metaobject as per-entity-type JSON buckets of { gid, savedAt } — one bucket each for product, variant, collection, metaobject, article, and page, with _ext_1..4 overflow buckets for large lists. V1 keeps a single default list per customer; the customer index metafield savelayer.lists (list.metaobject_reference) points at that list.
Reads differ by surface: Online Store themes read the save_list metaobject server-side in Liquid; headless and customer-account clients read through the SaveLayer API. Saved state is never exposed through a Shopify public (Storefront or Customer Account) API — all SaveLayer metaobjects are storefront = none / customerAccount = none.
Current repository shape
apps/pages: Shopify React Router app, app proxy routes, direct API routes, admin UIapps/worker: Cloudflare Worker for shared background, saved-state sync, and gatekeeping logicpackages/contracts: shared Zod schemaspackages/db: Prisma ORM and migrations for D1 (sessions, installs, usage,saved_state)packages/sdk: browser-safe SaveLayer SDKextensions/savelayer-theme: Shopify theme app extension