Service modes

Every commerce and pages domain resolves through a service key that defaults to mock, so a theme runs locally with no credentials — and production flips the keys to api, one domain at a time.

This is the single most useful thing to know on day one.

Every commerce and pages domain in a theme — products, categories, prices, inventory, cart, orders, accounts, addresses, organizations, teams, auth, payment, shipping, markets, order lists — resolves through a service key. In development every one of those keys defaults to mock, served from fixture data bundled with the base layer.

First run
npm install
npm run dev

That's it. You get a working storefront with a catalog, product pages, a cart, and a checkout — with no API key, no tenant, and not a single environment variable.

The practical consequence: onboarding a developer onto a theme project is a git clone and an npm install. Don't provision credentials before you need them. You can build blocks, style pages, review layouts with a designer, and demo the thing on a plane.

How the switch works

Each domain has a key in app.config.ts. The base layer ships every key set to mock:

the base layer's defaults
export default defineAppConfig({
  productService: 'mock',
  categoryService: 'mock',
  priceService: 'mock',
  inventoryService: 'mock',
  cartService: 'mock',
  orderService: 'mock',
  accountService: 'mock',
  authService: 'mock',
  // …and so on for every domain
})

Your theme's own app.config.ts merges over that, key by key. Setting a key to api swaps in the implementation that calls the platform through the public gateway:

app/app.config.ts
export default defineAppConfig({
  productService: 'api',
  categoryService: 'api',
  // everything you don't mention stays on mock
})

Two values per domain, and the second one is the same word everywhere:

Key valueWhat it resolves to
mockBundled fixture data, served from the layer's own assets. No network, no credentials.
apiThe live implementation, calling the platform through https://api.revenexx.com from your server routes.

The domains

KeyDomain
productServiceProduct list and product detail
categoryServiceCategories and the category tree
priceServicePrices
inventoryServiceStock and availability
cartServiceThe cart, and multi-cart when live
orderServiceOrders
orderListServiceSaved order lists
accountServiceThe buyer's profile
addressServiceThe address book
organizationServiceThe buyer's organization
contactsServiceContacts within the organization
authServiceLogin, registration, recovery
paymentServicePayment methods
shippingServiceShipping methods and rates
marketServiceMarkets and currencies
themeServiceThe tenant's branding

schemaService sits alongside these and resolves the form schemas the checkout renders; its default is local-file.

Why per-domain, not one global switch

Because "all mocks" and "full production wiring" are both the wrong place to be for most of a project.

Flip productService and categoryService to api while you build the product pages against a customer's real catalog, and leave the cart, orders, and account on mock — so you're not creating real orders in a customer's tenant every time you test the checkout button. Then flip the cart when you get to the cart.

This is also how you isolate a problem. If the product grid breaks after you go live, set productService back to mock for one request: if the grid renders, the bug is in the data path, not the block.

Switching at runtime

You don't have to restart to change modes. The dev panel writes a cookie the server reads per request, so a domain can be switched from the browser while the app is running. Resolution order for each domain:

  1. A per-domain override in the dev-mode cookie.
  2. The global mode in the cookie — live picks the domain's live key, mock forces the mock.
  3. The key in app.config.ts. The layer default is mock.

That's a development convenience. The value that ships is the one in app.config.ts.

What api needs from you

Two things, both set as Nuxt runtime config:

.env
NUXT_REVENEXX_API_URL=https://api.revenexx.com
NUXT_REVENEXX_TENANT=<your-tenant-slug>
NUXT_REVENEXX_API_KEY=<your-api-key>
The API key is server-side only. It lives in private runtime config, never in runtimeConfig.public, and it is read exclusively by the theme's server routes. A component that fetches api.revenexx.com directly would need the key in the browser, which is why components call /api/* on your own origin instead. See The BFF pattern and Authentication.

On a deployed site you don't set these by hand at all: the platform injects a scoped key and endpoint for you. See Environment variables.

In production

Production builds run with the keys set to api, so the mock path never serves a customer. That's a configuration fact rather than a build-time one — the mock implementations are still in the bundle, they're just not selected.

Two habits keep this honest:

  • Commit the production app.config.ts. The keys that ship should be in version control, not set by whoever last ran the deploy.
  • Check it on a preview. A preview host runs the real build with the real configuration. If a domain is still on mock there, you'll see fixture product names — which is much easier to catch before a domain points at the site. See Previews.

Next steps

Was this page helpful?