JS Client Library
@sovecom/client-js is a thin, zero-dependency fetch wrapper generated from the SovEcom OpenAPI spec. It gives you path-/query-/body-typed requests against every endpoint in the API, a built-in error class, and two convenience methods for checkout and guest order lookup.
See the architecture overview for how the API is structured, and the auto-generated API reference for the full endpoint list.
Installation
Section titled “Installation”pnpm add @sovecom/client-jsConstructing the client
Section titled “Constructing the client”Import createSovEcomClient and pass a SovEcomClientOptions object:
import { createSovEcomClient } from '@sovecom/client-js';
const client = createSovEcomClient({ baseUrl: 'https://api.example.com',});Options
Section titled “Options”| Option | Type | Required | Description |
|---|---|---|---|
baseUrl | string | yes | API origin, e.g. https://api.example.com. Trailing slash is stripped automatically. |
getToken | () => string | null | undefined | Promise<…> | no | Returns the customer bearer JWT. Called before each request; the result is sent as Authorization: Bearer <token>. Per-request Authorization headers are never overwritten. |
headers | Record<string, string> | no | Default headers merged into every request (e.g. a cart session token). |
fetch | typeof fetch | no | Override the fetch implementation. Defaults to globalThis.fetch. Pass a custom implementation for Node 18 and earlier or for tests. |
Authenticated client (customer)
Section titled “Authenticated client (customer)”Pass getToken to send a customer JWT on every request:
import { createSovEcomClient } from '@sovecom/client-js';
const client = createSovEcomClient({ baseUrl: process.env.API_URL!, getToken: () => sessionStorage.getItem('token') ?? undefined,});Admin client
Section titled “Admin client”Admin endpoints live under /admin/v1/. Pass the admin bearer token the same way:
import { createSovEcomClient } from '@sovecom/client-js';
const adminClient = createSovEcomClient({ baseUrl: process.env.API_URL!, getToken: async () => { // Return a cached admin JWT; refresh it here if needed. return process.env.ADMIN_TOKEN; },});Calling endpoints
Section titled “Calling endpoints”All endpoints go through client.request():
request<P extends PathFor<M>, M extends HttpMethod, TResponse = unknown>( method: M, path: P, options?: RequestOptions<P, M>,): Promise<TResponse>path is constrained to paths defined in the OpenAPI spec for the given method, so invalid paths are a compile error. TResponse defaults to unknown — the spec does not carry response body schemas, so you supply the type you expect.
Store API — list published products
Section titled “Store API — list published products”import { createSovEcomClient } from '@sovecom/client-js';import type { components } from '@sovecom/client-js';
// Supply your own response shape; the spec does not carry response bodies.type ProductListResponse = { data: components['schemas']['CreateProductDto'][]; nextCursor: string | null;};
const client = createSovEcomClient({ baseUrl: 'https://api.example.com' });
const result = await client.request< '/store/v1/products', 'get', ProductListResponse>('get', '/store/v1/products', { query: { pageSize: 20 },});
console.log(result.data);Store API — get a product by slug
Section titled “Store API — get a product by slug”const product = await client.request< '/store/v1/products/{slug}', 'get', { slug: string; title: string; status: 'draft' | 'published' | 'archived' }>('get', '/store/v1/products/{slug}', { path: { slug: 'my-cool-product' },});Store API — search
Section titled “Store API — search”The /store/v1/search endpoint accepts query parameters typed directly from the spec:
const results = await client.request<'/store/v1/search', 'get'>('get', '/store/v1/search', { query: { q: 'running shoes', sort: 'price_asc', minPrice: 5000, // integer cents maxPrice: 20000, currency: 'EUR', pageSize: 10, },});sort is typed as 'relevance' | 'price_asc' | 'price_desc' | 'newest' — the compiler rejects any other string.
Admin endpoints
Section titled “Admin endpoints”Admin calls are identical in shape. The client sends Authorization: Bearer <token> from getToken automatically.
Create a product
Section titled “Create a product”import type { components } from '@sovecom/client-js';
type CreateProductDto = components['schemas']['CreateProductDto'];
const newProduct = await adminClient.request< '/admin/v1/products', 'post'>('post', '/admin/v1/products', { body: { title: 'Canvas Tote Bag', slug: 'canvas-tote-bag', status: 'draft', isBundle: false, variants: [ { sku: 'CTB-NAT-OS', priceAmount: 2999, // 29.99 EUR in integer cents currency: 'EUR', stockQuantity: 100, allowBackorder: false, options: { color: 'natural' }, position: 0, }, ], } satisfies CreateProductDto,});Money values are always integer cents + currency code — never floats.
Admin login
Section titled “Admin login”import type { components } from '@sovecom/client-js';
type LoginDto = components['schemas']['LoginDto'];
const session = await adminClient.request< '/admin/v1/auth/login', 'post'>('post', '/admin/v1/auth/login', { body: { email: 'admin@example.com', password: 'hunter2', } satisfies LoginDto,});Convenience methods
Section titled “Convenience methods”Two high-level methods cover the most common storefront flows.
checkout(cartId)
Section titled “checkout(cartId)”Creates an order from an existing cart. The cart cookie token must be supplied via headers or getToken:
const order = await client.checkout<{ orderNumber: string }>(cartId);This calls POST /store/v1/carts/{cartId}/checkout with cartId URL-encoded in the path.
getOrderByNumber(orderNumber, orderToken)
Section titled “getOrderByNumber(orderNumber, orderToken)”Guest order lookup. The orderToken is sent in the X-Order-Token header — it is never placed in the URL:
const order = await client.getOrderByNumber<{ status: string }>( 'ORD-2026-00042', 'tok_abc123',);Typed responses
Section titled “Typed responses”The generated components and operations types are exported directly from @sovecom/client-js:
import type { components, operations, paths } from '@sovecom/client-js';
// DTO for a request bodytype CreateProductDto = components['schemas']['CreateProductDto'];
// Query parameters for the search endpointtype SearchQuery = operations['SearchStoreController_search']['parameters']['query'];
// All paths that support GETimport type { PathFor } from '@sovecom/client-js';type GetPaths = PathFor<'get'>;Response body types are not generated — the API spec does not carry response schemas. Define your own response interfaces and pass them as the TResponse type parameter. This keeps the types honest: if you supply a type, you own the claim that it matches what the server actually returns.
Error handling
Section titled “Error handling”request() throws SovEcomApiError for any non-2xx response:
import { createSovEcomClient, SovEcomApiError } from '@sovecom/client-js';
const client = createSovEcomClient({ baseUrl: 'https://api.example.com' });
try { const product = await client.request<'/store/v1/products/{slug}', 'get'>( 'get', '/store/v1/products/{slug}', { path: { slug: 'does-not-exist' } }, );} catch (err) { if (err instanceof SovEcomApiError) { console.error(err.status); // e.g. 404 console.error(err.statusText); // e.g. "Not Found" console.error(err.body); // parsed JSON body, or raw text if not JSON } else { throw err; // network error, abort, etc. }}The error message is SovEcom API <status> <statusText> and err.name is 'SovEcomApiError'.
Cancellation
Section titled “Cancellation”Pass an AbortSignal per request to support timeouts or user-initiated cancellation:
const controller = new AbortController();setTimeout(() => controller.abort(), 5000);
const result = await client.request<'/store/v1/products', 'get'>( 'get', '/store/v1/products', { signal: controller.signal },);Regenerating types after API changes
Section titled “Regenerating types after API changes”The types in src/generated/api.ts are generated from the API’s OpenAPI spec. After any API schema change:
# In apps/api — dump the updated specpnpm openapi:dump
# In packages/client-js — regenerate the TypeScript typespnpm generateCI enforces a diff guard: if the spec and the generated types diverge, the build fails.
See also:
Last updated: 2026-06-25