@cimplify/sdk
v0.57.0
Published
Cimplify Commerce SDK for storefronts
Maintainers
Readme
@cimplify/sdk
The official TypeScript SDK for building storefronts with the Cimplify Commerce Engine. Typed fetch client + 90+ React components + Server-Components helpers + an in-process mock that mirrors the production API.
Where to start
| Goal | Read |
|---|---|
| Scaffold a storefront and ship it | Storefronts guide |
| Architectural rules + agent contract | AGENTS.md |
| Step-by-step task walkthroughs | RECIPES.md |
| SDK ↔ backend schema contract | CONTRACTS.md |
| Pricing flow internals | ./sdk-pricing-flow.md |
Installation
bun add @cimplify/sdk # bun (recommended)
npm install @cimplify/sdk
yarn add @cimplify/sdkPublic exports
| Subpath | Use |
|---|---|
| @cimplify/sdk | Browser-safe typed client (createCimplifyClient), domain types, Result<T, E> |
| @cimplify/sdk/react | 90+ components + 30+ hooks (useCart, useProducts, <CataloguePage>, <CartDrawer>, …) |
| @cimplify/sdk/server | Server Components / Server Actions: getServerClient(), tags.*, revalidate* |
| @cimplify/sdk/utils | Money helpers, formatting, slug helpers |
| @cimplify/sdk/advanced | Lower-level escape hatches |
| @cimplify/sdk/mock | Programmatic mock for tests / MSW |
| @cimplify/sdk/mock/msw | MSW handlers wrapping the in-process mock |
| @cimplify/sdk/testing | Test harness — zod schemas, createTestClient, fixtures, assertX helpers |
| @cimplify/sdk/testing/suite | Pre-baked vitest suites: createBrandSuite, createCartFlowSuite, createContractSuite |
| @cimplify/sdk/styles.css | Pre-compiled Tailwind utilities for the React components |
Mock storefront API
This package ships an embeddable mock storefront API used by tests and local dev:
bunx @cimplify/sdk mock --seed <default|retail|fashion|…> # standalone HTTP server on :8787Or import it programmatically via @cimplify/sdk/mock / @cimplify/sdk/mock/msw.
Component registry
@cimplify/sdk ships a registry of ejectable components at ./registry/*.json (60+ entries) — same shadcn-style pattern: cimplify add product-grid copies the component source into your project so you can edit it, with imports already rewritten to @cimplify/sdk / @cimplify/sdk/react.
The registry data lives here; the add / list commands live in @cimplify/cli.
CLI moved
Everything else — init, login, projects, link, deploy, env, domains, logs, etc. — lives in the dedicated CLI package:
npm i -g @cimplify/cli # one-time install
cimplify --help # full surface
# Or zero-install:
bunx @cimplify/cli deploySee @cimplify/cli for the full subcommand list.
Quick start
import { createCimplifyClient } from "@cimplify/sdk";
const client = createCimplifyClient({
publicKey: process.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY,
});
// Catalogue
const products = await client.catalogue.getProducts({ limit: 24 });
if (!products.ok) throw products.error;
const single = await client.catalogue.getProductBySlug("studio-tee-natural");
const variants = await client.catalogue.getVariants("prod_xyz");
// Cart — flat add-to-cart payload (variant + add-ons go on the body, not nested)
await client.cart.addItem({
item_id: "prod_studio-tee-natural",
quantity: 1,
variant_id: "var_studio-tee-natural_size_s",
add_on_options: ["addopt_gift_wrap"],
special_instructions: "Please ship without invoice.",
});
const cart = await client.cart.get();
await client.cart.updateQuantity("ci_…", 2);
await client.cart.removeItem("ci_…");
await client.cart.applyCoupon("WELCOME10");
// Auth — single contact arg, then `(contact, code)` to verify
await client.auth.requestOtp("+233244000000");
await client.auth.verifyOtp("+233244000000", "123456");
await client.auth.logout();Every call returns Result<T, CimplifyError>. Always check .ok:
const r = await client.catalogue.getProducts();
if (!r.ok) {
console.error(r.error.code, r.error.message);
return;
}
console.log(r.value.items);React components
"use client";
import {
CimplifyProvider,
CartDrawerProvider,
CartDrawer,
CataloguePage,
ProductPage,
CartPage,
CheckoutPage,
CimplifyAccount,
useCart,
useCartDrawer,
} from "@cimplify/sdk/react";
function Root({ children }) {
return (
<CimplifyProvider client={client}>
<CartDrawerProvider>
{children}
<CartDrawer onCheckout={() => router.push("/checkout")} />
</CartDrawerProvider>
</CimplifyProvider>
);
}90+ components ship — variant-aware product cards, customizers (variant / add-on / bundle / composite / billing-plan / customer-input / scheduling), full pages (<CataloguePage>, <ProductPage>, <CartPage>, <CheckoutPage>, <SearchPage>, <CimplifyAccount>), cart drawer with free-shipping progress, primitives (<Price>, <QuantitySelector>, <DealBanner>, <DeliveryEstimate>, <ChatWidget>).
Server Components (Next.js App Router)
import { getServerClient, tags, revalidateProducts } from "@cimplify/sdk/server";
import { unstable_cacheTag as cacheTag, unstable_cacheLife as cacheLife } from "next/cache";
async function getCatalogue() {
"use cache";
cacheTag(tags.products());
cacheLife("hours");
const r = await getServerClient().catalogue.getProducts({ limit: 24 });
if (!r.ok) throw new Error(r.error.message);
return r.value.items;
}
// In a Server Action after a mutation:
"use server";
async function refreshProducts() {
await revalidateProducts();
}Testing harness
Every scaffolded storefront ships with a 30-second feedback loop:
bun run check # typecheck + lint + brand + cart-flow + contract suites
bun run check:brand # schema invariants on lib/brand.ts
bun run check:cart # add → dedupe → remove against in-process mock
bun run check:contract # validates payload + response shapes
bun run check:visual # Playwright snapshots (fashion ref template)Custom test cases use the extend hook on the suites:
import { createCartFlowSuite } from "@cimplify/sdk/testing/suite";
import { brand } from "../lib/brand";
import { expect } from "vitest";
createCartFlowSuite({
seed: brand.mock.seed,
businessId: brand.mock.businessId,
extend: ({ getHandle, it }) => {
it("every product priced in GHS", async () => {
const list = await getHandle().client.catalogue.getProducts();
if (!list.ok) throw list.error;
for (const p of list.value.items ?? []) expect(p.currency).toBe("GHS");
});
},
});The mock is the oracle. createTestClient({ seed }) boots the same Hono mock you run locally, in-process, via the SDK's first-class fetch injection — parallel-safe, no global mutation. See RECIPES.md for the full workflow.
Money
All monetary values use the branded Money type — string at runtime, distinct from plain string at compile time.
import { money, formatPrice, isOnSale, getDiscountPercentage, type Money } from "@cimplify/sdk";
const price = money("29.99");
parseFloat(price); // 29.99
formatPrice(29.99, "GHS"); // "GH₵29.99"
if (isOnSale(product)) console.log(`${getDiscountPercentage(product)}% off`);Error handling
import { CimplifyError } from "@cimplify/sdk";
const r = await client.cart.addItem({ item_id: "invalid", quantity: 1 });
if (!r.ok && r.error instanceof CimplifyError) {
console.error(r.error.code, r.error.message, r.error.retryable);
}For schema-shape errors (in tests, contract validation), assertX helpers throw a typed SchemaViolationError with structured issues[] and an RFC 7807 toJSON():
import { assertCart, SchemaViolationError } from "@cimplify/sdk/testing";
try {
assertCart(response);
} catch (e) {
if (e instanceof SchemaViolationError) console.error(e.toJSON());
}TypeScript
Domain types are exported from the root:
import type {
Product, ProductWithDetails, ProductVariant,
Category, Collection, Bundle, Composite,
Cart, CartItem, Order, Customer,
} from "@cimplify/sdk";
// Schema-derived types from the testing entry:
import type { Brand, AddItemPayload, CheckoutResponse } from "@cimplify/sdk/testing";License
MIT
