cimplify
CLI

cimplify mock

Boots an in-process Hono server that mirrors the production Cimplify API (~99% parity) with seeded data. The mock is the oracle for testing and local development; every scaffolded storefront wires `bun dev` to start it alongside Next.js.

Usage

The mock storefront API lives in @cimplify/sdk (not @cimplify/cli) because it's tightly coupled to the SDK's domain types and seed data. Boot it three ways — the cimplify mock CLI wrapper (recommended; finds the SDK bin for you and auto-loads a local seed file), the cimplify-mock bin directly, or bunx with no install.

# Via the CLI wrapper (auto-loads ./cimplify.seed.{ts,json} if present)
cimplify mock                                 # default seed, 127.0.0.1:8787
cimplify mock --seed retail

# Zero-install via bunx
bunx @cimplify/sdk mock --seed restaurant

# Or install once and call the bin directly
bun add -g @cimplify/sdk
cimplify-mock --seed retail

Seeds

SeedUse case
defaultA representative bakery; the default if you omit --seed.
restaurantMenu-driven, with reservations + add-ons.
retailVariant-aware retail with full PDPs.
servicesBookable services with calendar availability.
groceryHigh-SKU grocery with quick-add.
fashionMulti-drop fashion with a lookbook.
reesa-storefrontReesa-specific seed for AI / agent demos.
emptyA bare business with no products. Useful for onboarding flows.

Custom seeds

The built-in seeds are a starting point, not a ceiling. To spin up your own demo site — a specific bakery, a client's spa, a stress-test catalogue — supply your own seed. No SDK fork, no patching node_modules. There are three ways, from zero-code to full control; pick the one that fits.

Quickest: scaffold from a built-in

cimplify mock init writes an editable seed into the current directory, starting from the closest built-in. Then cimplify mock picks it up automatically.

cimplify mock init --from retail            # writes cimplify.seed.ts
cimplify mock init --from retail --format json   # writes cimplify.seed.json instead
cimplify mock                               # auto-loads ./cimplify.seed.{ts,json}

cimplify mock (and cimplify-mock) auto-load a cimplify.seed.json, cimplify.seed.mjs, cimplify.seed.js, or cimplify.seed.ts in the working directory when no --seed* flag is given. Drop the file in, restart, done. An explicit --seed/--seed-file/--seed-module flag overrides the convention.

Tier 1 — JSON snapshot (no code)

The mock can dump its entire seeded state to a JSON file. Edit that file — rename the business, change prices, swap images, delete products — and boot from it. Pure data, no build step, hot-swappable.

cimplify mock --seed default --dump akua.json   # 1. dump a starting point
$EDITOR akua.json                               # 2. edit the data
cimplify mock --seed-file akua.json             # 3. boot from your data

The default business is the snapshot's sole business. If a snapshot has several businesses, set which one is the default with a top-level __meta block:

akua.json (excerpt)
{
  "__meta": { "defaultBusinessId": "bus_akua" },
  "businesses": { "bus_akua": { "id": "bus_akua", "name": "Akua's Bakery", "...": "..." } },
  "products":   { "prod_sourdough": { "id": "prod_sourdough", "name": "Sourdough", "...": "..." } }
}

Keys that aren't stores (like __meta) are ignored by the loader, so the sidecar is safe to keep.

Tier 2 — Seed module (full power)

For logic — loops, generated variants, computed pricing — write a module that default-exports a seed function. defineSeed types it; the ctx builder mints ids, applies sensible defaults, and wires category/collection back-references for you.

cimplify.seed.ts
import { defineSeed } from "@cimplify/sdk/mock";

export default defineSeed((ctx) => {
  const biz = ctx.business({ name: "Akua's Bakery", handle: "akua", default_currency: "GHS" });
  const breads = ctx.category({ name: "Breads" });

  for (const [name, price] of [["Sourdough", "25.00"], ["Baguette", "15.00"]] as const) {
    ctx.product({ name, price, category: breads, image: ctx.image("bakery", "sourdough-loaf") });
  }

  return { businessId: biz.id };
});
cimplify mock --seed-module ./cimplify.seed.ts   # or just `cimplify mock` (convention)

.mjs and .js modules load under any runtime; .ts modules need bun (run via bunx @cimplify/sdk mock or cimplify mock from a bun project) or another TS loader. If you're on plain node, use a .mjs or .json seed.

Tier 3 — Extend a built-in

Start from a built-in seed inside a module, then mutate it — the fastest way to a distinct demo without authoring a whole catalogue.

cimplify.seed.ts
import { defineSeed } from "@cimplify/sdk/mock";

export default defineSeed((ctx) => {
  const { businessId } = ctx.extend("restaurant");   // full restaurant catalogue
  ctx.product({ name: "Chef's Tasting Menu", price: "180.00" });  // then add your own
  return { businessId };
});

The ctx builder API

defineSeed((ctx) => …) receives a SeedContext. Every method mints an id, fills defaults, and stores the record; products auto-link to the category / collection you pass.

MethodCreates
ctx.business({ name, … })The storefront's business (also becomes the default business).
ctx.category({ name, slug?, … })A catalogue category.
ctx.collection({ name, slug?, … })A merchandising collection.
ctx.product({ name, price, category?, collection?, image?, … })A product, back-linked to its category/collection.
ctx.service({ name, price, durationMinutes })A bookable service.
ctx.image(industry, slug)A seed image URL on the Cimplify CDN.
ctx.extend(name)Applies a built-in seed first; returns { businessId }.
ctx.registryThe raw state registry — escape hatch for anything not modeled above.

The seed function returns { businessId }, naming the business the mock treats as the signed-in tenant.

Programmatic (in-process)

createMockApp (from @cimplify/sdk/mock) takes the same sources directly — useful in tests or custom harnesses:

import { createMockApp, defineSeed } from "@cimplify/sdk/mock";

const app = createMockApp({ seed: "retail" });                       // built-in name
const app2 = createMockApp({ seed: { kind: "json", data: snapshot } });   // JSON snapshot
const app3 = createMockApp({ seed: { kind: "fn", seed: defineSeed((ctx) => { /* … */ }) } });

Flags

FlagDefaultDescription
--port <num>8787Listen port.
--host <host>127.0.0.1Bind host. Use 0.0.0.0 to expose on a LAN.
--seed <name>defaultOne of the built-in seeds in the table above.
--seed-file <file.json>noneBoot from a JSON snapshot instead of a built-in. See Custom seeds.
--seed-module <file>noneBoot from a module that default-exports a seed function (.mjs/.js anywhere; .ts needs bun).
--dump <file.json>noneSeed, write the full state snapshot to <file>, and exit (no server). The way to create a --seed-file.
--auth-mode <mode>permissivepermissive accepts any token; strict enforces real OTP / session checks.
--otp <code>123456Default OTP code accepted by auth.verifyOtp.
--persist <file>nonePersist mock state to a JSON file across restarts.
--cors <origins>noneComma-separated allow-list, or * for any origin.
--webhook-url <url>nonePOST mock events to this URL.
--webhook-secret <key>noneHMAC secret for webhook signatures.
--frozen-at <iso>nowFreeze the mock's clock at a given ISO timestamp.
--quietoffSuppress per-request logs.

Examples

Frozen-clock testing
# All time-sensitive flows (slot availability, deal expiry, fx quote ttl) anchor to this point.
cimplify mock \
  --seed retail \
  --frozen-at "2026-06-01T10:00:00Z"
LAN-accessible mock with persistence
cimplify mock \
  --host 0.0.0.0 \
  --port 8787 \
  --seed grocery \
  --persist ./.mock-state.json
Webhook delivery during local development
cimplify mock \
  --seed restaurant \
  --webhook-url http://localhost:3000/api/webhooks \
  --webhook-secret whsec_local_dev
Strict auth + custom OTP
cimplify mock \
  --auth-mode strict \
  --otp 042042

In Next.js dev

Every storefront template wires bun dev to spawn the mock + next dev in parallel. next.config.ts proxies /v1/* to the mock to avoid CORS in the browser.

package.json (excerpt)
{
  "scripts": {
    "dev": "concurrently \"bun run dev:mock\" \"next dev\"",
    "dev:mock": "cimplify mock --seed retail --quiet"
  }
}

Programmatic mock

For unit / integration tests you don't need a separate process; the mock runs in-process via the SDK's first-class fetch injection, parallel-safe and globalThis-free.

A test using @cimplify/sdk/testing
import { createTestClient, fixtures, assertCart } from "@cimplify/sdk/testing";

const h = await createTestClient({ seed: "retail" });
await fixtures.addFirstProduct(h.client);
const cart = await h.client.cart.get();
assertCart(cart);

Where next

On this page