@viu/emporix-sdk
v2.18.0
Published
TypeScript SDK for the Emporix Commerce Engine
Readme
@viu/emporix-sdk
Framework-agnostic TypeScript SDK for the Emporix Commerce Engine. Native
fetch only (Node 20.19+), zero runtime dependencies.
Install
pnpm add @viu/emporix-sdkQuick start
import { EmporixClient, auth } from "@viu/emporix-sdk";
const sdk = new EmporixClient({
tenant: "mytenant",
credentials: {
backend: { clientId: "...", secret: "..." },
storefront: { clientId: "..." },
custom: { partner: { clientId: "...", secret: "..." } },
},
});
// Anonymous browse
const products = await sdk.products.list();
// Authenticated shopper
const { customerToken } = await sdk.customers.login({ email, password });
const ctx = auth.customer(customerToken);
const me = await sdk.customers.me(ctx);
const cart = await sdk.carts.create({ currency: "EUR" }, ctx);
await sdk.carts.addItem(cart.id, { productId: "p_1", quantity: 2 }, ctx);
// Service account with a custom credential set
await sdk.products.list(undefined, auth.service("partner"));
// Externally-issued token (SSO/token-exchange done upstream)
await sdk.customers.me(auth.raw(externalJwt));Services
The client exposes the full Emporix surface as namespaced services. Each is a
property on the EmporixClient instance:
| Area | Services |
| --- | --- |
| Catalog | products, categories, prices, brands, labels, catalogs |
| Cart & checkout | carts, checkout, payments, coupons, taxes, shipping, fees |
| Orders & fulfilment | orders, salesOrders, returns, pickPack, availability, indexing |
| Customers & B2B | customers, customerAdmin, companies, contacts, locations, customerGroups, approvals, rewardPoints, segments |
| Platform & config | sites, sessionContext, tenantConfig, clientConfig, media, schemas, webhooks, sequentialIds, units, countries, currencies, vendors, shoppingLists, sepaExport, ai, ragIndexer |
The sections below highlight the most-used services; per-service guides live in
../../docs/.
Searching by mixin (custom) fields
products.search, categories.search, orders.listMine({ q }),
customerAdmin.searchCustomers({ q }) and vendor.searchVendors({ q }) accept a
raw Emporix q string or a type-safe filter built with mixinQuery from
@viu/emporix-mixins. The filter is entity-gated (a filter built for
one entity is a compile error on another) and an or() filter is rejected on
services that don't support compoundLogicalQuery.
import { mixinQuery } from "@viu/emporix-mixins";
import { mixins } from "./mixins/generated/registry";
const q = mixinQuery(mixins.attrs, { color: "Blue", qty: { gte: 10 } });
const page = await sdk.products.search(q);See ../../docs/mixin-search.md for the capability matrix.
Configuration
| Option | Default | Notes |
| --- | --- | --- |
| tenant (required) | — | lowercase, 3–16 chars, ^[a-z][a-z0-9]+$ |
| credentials.backend (required) | — | { clientId, secret, scope? } — service token |
| credentials.storefront | — | { clientId } — anonymous token (no secret) |
| credentials.custom | — | Record<name, { clientId, secret, scope? }> |
| host | https://api.emporix.io | |
| timeouts | { connectMs: 10000, readMs: 60000 } | |
| retry | { maxAttempts: 3 } | 5xx/429 backoff + jitter for idempotent methods (GET/PUT/DELETE; POST/PATCH opt in via idempotent: true); numeric Retry-After honoured, capped at 8s |
| cache | { expirationBufferSeconds: 60, maxLifetimeSeconds: 3600 } | token cache |
| logger | console @ warn | false, a Logger, or { level, services, pretty, redact } |
| tokenProvider | built-in | inject for SSO/token-exchange |
AuthContext per method
auth.service(name?), auth.anonymous(), auth.customer(token),
auth.raw(token). The last argument of every service method is the
AuthContext; defaults below apply when omitted.
| Method | Default | Required kind |
| --- | --- | --- |
| customers.signup / .login / .requestPasswordReset / .confirmPasswordReset | anonymous | — |
| customers.me / .update / .changePassword / .addresses.* | — | customer or raw |
| customers.anonymous() | — | obtains anonymous session (token ignored) |
| products.* / categories.* (reads) | anonymous | — (pass customer for personalized pricing) |
| carts.* | — | explicit customer or anonymous |
| carts.merge | — | customer |
| companies.* / contacts.* / locations.* / customerGroups.* (B2B) | — | customer (reads need *_read_own; mutations need *_manage) |
| media.* (Asset CRUD + download) | service | service — server-only (media.asset_read / media.asset_manage); never call from the browser |
AuthContext is per call, never stored — one client safely serves many
concurrent shoppers (SSR/edge/multi-tenant). SDK-managed (service/anonymous)
401s refresh-and-retry once; caller-managed (customer/raw) 401s propagate as
EmporixAuthError. Full details in ../../docs/auth.md.
Logging
Structured, level-aware, per-service controllable, secret-redacting, zero
dependency. See ../../docs/logging.md.
Checkout & payment
sdk.checkout.placeOrder(...) / .placeOrderFromQuote(...) and
sdk.payments.listPaymentModes(...) / .authorize(...). The saas-token
header, guest checkout and deferred payment are covered in
../../docs/checkout.md.
B2B
sdk.companies (legal entities), sdk.contacts (contact assignments),
sdk.locations (HQ/warehouse/office), sdk.customerGroups (IAM groups,
read-only for now). Switching company scope is a customer-token rescope via
sdk.customers.refresh({ refreshToken, legalEntityId }). Mutations that the
customer's role lacks scope for surface as EmporixInsufficientScopeError
(extends EmporixForbiddenError, carries requiredScope). See
../../docs/b2b.md.
Media
sdk.media covers the full /media/{tenant}/assets/* surface: create
(uploadFile / link / create), list (paginated, PaginatedItems<Asset>),
get, update (JSON metadata or BLOB multipart file-replacement via the
replaceFile sugar), remove, and download (resolves to either a redirect
URL for PUBLIC assets or an ArrayBuffer for PRIVATE). All endpoints
require a server-only scope — every call defaults to a service
AuthContext. Storefronts read media via product.productMedia (denormalised
on the product) or the useProductMedia(productId) hook, not by calling
the Media service directly. See ../../docs/media.md.
Availability
sdk.availability reads site-aware product availability: get(productId, siteCode)
for one product and getMany(productIds, siteCode) for a batch (single
POST .../search request, result in input order). The opt-in
defaultAvailableOnNotFound returns { available: true } for products without a
stock record. There is no restock-date field. See ../../docs/availability.md.
Subpath exports
@viu/emporix-sdk (the package root) re-exports everything — every service,
type, and helper. Tree-shakeable subpaths are published for the high-traffic
services: ./customer, ./product, ./category, ./cart, ./checkout,
./payment, ./price, ./media, ./segment, ./companies, ./contacts,
./locations, ./customer-groups, ./orders, ./availability. All other
services (Tax, Coupon, RewardPoints, Shipping, Returns, Catalog, Vendor,
PickPack, CustomerAdmin, Approval, …) are reached from the package root.
Authors
- Dominic Fritschi — Maintainer — VIU
- Andreas Nebiker — Contributor — VIU
- The Team at VIU — Contributors — VIU
License
This project is licensed under the MIT License — see the LICENSE file for details.
