Typed TypeScript client for building headless storefronts (Next.js, Remix, Astro, native mobile apps, kiosks) against the BuilderBlack platform.
Heads up: this package is for frontend developers building shopper-facing storefronts. If you're building a partner app (analytics, ERP sync, marketing automations), use the partner API directly — see PUBLIC-API.md.
- Native
fetchonly — works in browser, Node 18+, edge runtimes - Dual ESM + CommonJS exports
- Zero runtime dependencies
- Auto Idempotency-Key on writes
- Auto X-Cart-Session capture from response headers
- Customer JWT + cart session managed client-side
npm install @builderblack/storefront-sdkimport { createStorefrontClient } from '@builderblack/storefront-sdk';
const sf = createStorefrontClient({
apiUrl: 'https://api.builderblack.com',
tenantSlug: 'kartse',
// Server-only — never expose in browser code. Issue from your
// dashboard at app.builderblack.com/settings/api-tokens.
storefrontToken: process.env.BB_STOREFRONT_TOKEN,
});
// List products
const { data: products } = await sf.products.list({ limit: 24 });
// Get one product by slug
const { data: product } = await sf.products.getBySlug('widget-pro');
// Add to cart (creates a guest session on first call)
const { data: cart } = await sf.cart.addItem({
variantId: product.variants?.[0].id,
quantity: 1,
});
// Save the cart session for future visits
localStorage.setItem('bb_cart_session', sf.getCartSessionId() ?? '');Public endpoints (products, collections, cart) work without auth.
Pass tenantSlug so we know which store. Subject to per-IP rate
limits (600 req/min default).
Bind a token to the client for elevated rate limits and longer cache TTLs (1h vs 5min):
const sf = createStorefrontClient({
apiUrl: 'https://api.builderblack.com',
tenantSlug: 'kartse',
storefrontToken: process.env.BB_STOREFRONT_TOKEN,
});Never expose the storefront token in browser JavaScript. It's a server-only credential. Use it in:
getServerSideProps/approuter server components in Next.js- Remix
loaderfunctions - Astro
Astro.localsserver-side fetches - Backend-for-frontend (BFF) layers in native apps
After a buyer logs in:
const { data } = await sf.customer.login({ email, password });
// `setCustomerToken` is called automatically by the SDK; subsequent
// /me/* calls authenticate as that customer.
const { data: profile } = await sf.customer.me();
const { data: orders } = await sf.customer.listOrders();
// On logout
sf.customer.logout();To persist the session across browser restarts, save the
refresh_token (in an httpOnly cookie or localStorage) and call
sf.customer.refreshAccessToken(refreshToken) on app boot.
Carts identify themselves one of two ways:
- Customer cart — when
setCustomerToken()has been called, the server usescustomer_idfrom the JWT. - Guest cart —
X-Cart-Session: <uuid>header. The SDK creates this automatically on the first cart write; you persist the UUID client-side and pass it back on the next page load.
// On first visit — no session yet
const sf = createStorefrontClient({ apiUrl, tenantSlug });
await sf.cart.addItem({ variantId: 'v1', quantity: 1 });
// Server set X-Cart-Session in the response; SDK captured it
const session = sf.getCartSessionId();
localStorage.setItem('bb_cart_session', session ?? '');
// On return visit
const sf = createStorefrontClient({
apiUrl,
tenantSlug,
cartSessionId: localStorage.getItem('bb_cart_session') ?? undefined,
});
const { data: cart } = await sf.cart.get(); // returns the persisted cartWhen a guest logs in, the server auto-merges their guest cart into their customer cart.
| Module | Methods |
|---|---|
sf.products |
list(params), getBySlug(slug), getById(id) |
sf.collections |
list(), getBySlug(slug), listProducts(id, params) |
sf.cart |
get(), setItems(items), addItem({variantId, quantity}), updateItem(id, qty), removeItem(id), clear() |
sf.checkout |
getShippingRates(address), getTaxQuote({address, subtotal}), validateDiscount({code, ...}), placeOrder(input), getConfirmation(orderId) |
sf.customer |
register(input), login(input), logout(), refreshAccessToken(refresh), me(), updateProfile(input), listAddresses(), addAddress(addr), listOrders(), getOrder(id), cancelOrder(id) |
sf.navigation |
get(menuKey) — 'header', 'footer', 'mobile', 'legal' |
sf.fx |
rates() |
sf.theme |
live() |
sf.raw |
Direct HTTP access (get / post / patch / put / delete) for endpoints not yet wrapped |
// 1. Get available shipping rates for the buyer's address
const { data: { methods } } = await sf.checkout.getShippingRates({
country: 'IN', state: 'Karnataka', postal_code: '560001', city: 'Bengaluru',
});
// 2. Get tax quote
const { data: tax } = await sf.checkout.getTaxQuote({
address: { country: 'IN', state: 'Karnataka', postal_code: '560001' },
subtotal: '1499.00',
});
// 3. Optionally validate a discount code
const { data: discount } = await sf.checkout.validateDiscount({
code: 'WEEKEND10',
subtotal: '1499.00',
items: [{ variant_id: 'v1', quantity: 1 }],
});
// 4. Place the order — Idempotency-Key auto-added by the SDK
const { data: order } = await sf.checkout.placeOrder({
customer: { email, first_name, last_name, phone },
shipping_address: { /* full address */ },
shipping_method: methods[0].id,
payment_method: 'razorpay',
discount_code: 'WEEKEND10', // optional
});
// 5. Hand off to payment provider using order.payment_intent
// (Razorpay checkout SDK / Stripe Elements / etc.)Failed requests throw StorefrontApiError:
import { StorefrontApiError } from '@builderblack/storefront-sdk';
try {
await sf.cart.addItem({ variantId, quantity: 99999 });
} catch (err) {
if (err instanceof StorefrontApiError) {
if (err.code === 'OUT_OF_STOCK') {
// Show "only N left" UI
}
if (err.code === 'UNAUTHORIZED') {
// Refresh customer token
}
console.error(err.status, err.code, err.message, err.details);
}
}See error code list.
The platform sets Cache-Control headers on GET responses:
- Anonymous:
public, max-age=60, s-maxage=300, stale-while-revalidate=600 - With Storefront API token:
s-maxage=3600(1h CDN edge cache)
Use the SDK in a server-side renderer behind a CDN (Vercel Edge, Cloudflare Workers, your own Varnish) to serve product pages at edge speed without polling our origin.
If you have a Hydrogen / Storefront-Kit codebase, see
STOREFRONT-API.md § 12
for a compatibility table.
MIT.
- Full API reference:
STOREFRONT-API.md - Reference Next.js storefront:
github.com/builderblack/storefront-starter - Bug reports:
partners@builderblack.com