supacommerce

Client

Full API reference for @supacommerce/client.

@supacommerce/client

A typed ecommerce query client that wraps your Supabase client and gives you an ecommerce-oriented API.

Installation

pnpm add @supacommerce/client @supabase/supabase-js

Setup

import { createClient as createSupabaseClient } from "@supabase/supabase-js";
import { createClient } from "@supacommerce/client";

const supabase = createSupabaseClient(
  process.env.VITE_SUPABASE_URL!,
  process.env.VITE_SUPABASE_ANON_KEY!,
);

export const commerce = createClient(supabase);

The client respects your Supabase auth session and RLS policies automatically. Customers can only see their own carts and orders. Products are publicly readable.

For admin operations that bypass RLS, pass the service role client:

const supabaseAdmin = createSupabaseClient(
  process.env.VITE_SUPABASE_URL!,
  process.env.VITE_SUPABASE_SERVICE_ROLE_KEY!,
);
const adminCommerce = createClient(supabaseAdmin);

commerce.cart

Carts require a Supabase auth user. For guest customers, use supabase.auth.signInAnonymously() — the cart is preserved when the user upgrades to a full account.

// Get or create the current customer's active cart
const cart = await commerce.cart.getOrCreate(regionId?, currencyCode?)

// Get a cart by ID
const cart = await commerce.cart.get(cartId)

// Add an item
// unitPrice is optional — resolved from the variant's price set if omitted
const cart = await commerce.cart.addItem(cartId, {
  variantId: "...",
  quantity: 1,
  unitPrice: 2999, // optional
})

// Update a line item quantity
const cart = await commerce.cart.updateItem(cartId, lineItemId, { quantity: 2 })

// Remove a line item
const cart = await commerce.cart.removeItem(cartId, lineItemId)

// Set addresses
const cart = await commerce.cart.setShippingAddress(cartId, address)
const cart = await commerce.cart.setBillingAddress(cartId, address)

// Set email — required before checkout
const cart = await commerce.cart.setEmail(cartId, "user@example.com")

// Select a shipping method
const cart = await commerce.cart.setShippingMethod(cartId, shippingOptionId)

// Apply / remove a promotion code
const cart = await commerce.cart.applyPromotion(cartId, "SAVE10")
const cart = await commerce.cart.removePromotion(cartId, "SAVE10")

// Initiate checkout — calls the cart-checkout edge function
const result = await commerce.cart.checkout(cartId, {
  paymentProvider: "manual", // replace with your provider
  billingAddress: address,   // optional
})
// result.orderId
// result.paymentSession.data — pass to your provider's client SDK

commerce.catalog

// List published products
const { data, count, hasMore } = await commerce.catalog.listProducts({
  limit: 20,
  offset: 0,
  categoryId: "...",      // filter by category
  collectionId: "...",    // filter by collection
  salesChannelId: "...",  // filter by sales channel
  search: "shirt",        // title search
})

// Get a product by ID
const product = await commerce.catalog.getProduct(productId)

// Get a product by URL handle
const product = await commerce.catalog.getProductByHandle("blue-t-shirt")

// Get a variant by ID
const variant = await commerce.catalog.getVariant(variantId)

// List categories — pass parentId for children, omit for root
const categories = await commerce.catalog.listCategories(parentId?)

// List collections
const { data } = await commerce.catalog.listCollections({ limit: 10 })

commerce.orders

// List the current customer's orders
const { data, count } = await commerce.orders.list({
  limit: 10,
  status: "completed",
})

// Get a single order
const order = await commerce.orders.get(orderId)

// Get by human-readable display ID (order number shown to customers)
const order = await commerce.orders.getByDisplayId(1042)

commerce.customers

// Get the current customer's profile
const customer = await commerce.customers.me()

// Update profile
const customer = await commerce.customers.updateProfile({
  firstName: "Jane",
  lastName: "Doe",
  phone: "+27821234567",
})

// Address management
const addresses = await commerce.customers.listAddresses()

const address = await commerce.customers.addAddress({
  address1: "123 Main St",
  city: "Cape Town",
  countryCode: "ZA",
})

const address = await commerce.customers.updateAddress(addressId, {
  isDefault: true,
})

await commerce.customers.deleteAddress(addressId)

commerce.inventory

// Get total available stock across all locations
const available = await commerce.inventory.getTotalAvailable(variantId)

// Get full availability breakdown by location
const availability = await commerce.inventory.getAvailability(variantId)
// availability.totalAvailable
// availability.isAvailable
// availability.levels[].locationName
// availability.levels[].quantityAvailable

// Check multiple variants efficiently in one query
const map = await commerce.inventory.getBulkAvailability([variantId1, variantId2])
const qty = map.get(variantId1) // number

commerce.pricing

All monetary amounts are integers in the smallest currency unit. Use formatCurrency from @supacommerce/utils to display them.

// Get the best price for a variant
// Respects price lists, regions, and volume/tiered pricing
const price = await commerce.pricing.getVariantPrice({
  variantId: "...",
  regionId: "...",
  currencyCode: "USD",
  quantity: 2, // for tiered pricing
})
// price.amount       — integer, smallest currency unit
// price.currencyCode
// price.priceListId  — non-null if a sale price was applied

// Get prices for multiple variants at once
const priceMap = await commerce.pricing.getBulkVariantPrices(
  [variantId1, variantId2],
  { regionId, currencyCode: "USD" },
)
const price = priceMap.get(variantId1)

commerce.promotions

// Validate a promotion code and calculate the discount
const result = await commerce.promotions.validate({
  code: "SAVE10",
  cartSubtotal: 5000,   // integer, smallest currency unit
  customerId: "...",    // optional — checks per-customer usage limit
})
// result.valid
// result.discountAmount
// result.reason — why it's invalid (if !valid)

// List automatic promotions (applied without a code)
const promos = await commerce.promotions.listAutomatic()

commerce.regions

// List all active regions
const regions = await commerce.regions.list()

// Get a region by ID
const region = await commerce.regions.get(regionId)

// Find the region for a country code
const region = await commerce.regions.getByCountry("ZA")

commerce.fulfillment

// List shipping options available for a region
// Filters out options with unmet min_subtotal requirements
const options = await commerce.fulfillment.listShippingOptions({
  regionId: "...",
  cartSubtotal: 5000,
})

// Get a single shipping option
const option = await commerce.fulfillment.getShippingOption(optionId)

commerce.tax

// Calculate tax for a subtotal and location
const tax = await commerce.tax.calculate({
  subtotal: 5000,
  countryCode: "ZA",
  provinceCode: "WC", // optional — matches province-specific rates first
})
// tax.taxTotal — integer
// tax.rate     — decimal, e.g. 0.15 for 15%

// List all tax regions for a country
const taxRegions = await commerce.tax.listTaxRegions("ZA")

commerce.salesChannels

const channels = await commerce.salesChannels.list()
const defaultChannel = await commerce.salesChannels.getDefault()
const channel = await commerce.salesChannels.get(channelId)

commerce.admin

Requires a service role client — see Setup.

// Get current admin's record — throws ForbiddenError if not admin
const me = await adminCommerce.admin.me()

// List all admins
const { data } = await adminCommerce.admin.list()

// Create, update, deactivate
const admin = await adminCommerce.admin.create({
  userId,
  email,
  role: "manager",
})
const admin = await adminCommerce.admin.update(adminId, { role: "admin" })
await adminCommerce.admin.deactivate(adminId)

Error handling

All methods throw typed errors from @supacommerce/utils:

import {
  NotFoundError,
  ValidationError,
  ForbiddenError,
  InventoryError,
} from "@supacommerce/utils";

try {
  const product = await commerce.catalog.getProduct(id);
} catch (err) {
  if (err instanceof NotFoundError) {
    // 404 — product doesn't exist or isn't published
  }
  if (err instanceof InventoryError) {
    // 422 — out of stock
  }
}

See Utils for the full list of error types.


Currency

All monetary amounts are integers in the smallest currency unit — cents for USD, pence for GBP, etc. Use @supacommerce/utils to convert and format:

import { formatCurrency, toMinorUnit, fromMinorUnit } from "@supacommerce/utils";

formatCurrency(2999, "USD"); // "$29.99"
formatCurrency(2999, "GBP"); // "£29.99"
formatCurrency(300, "JPY");  // "¥300"

toMinorUnit(29.99, "USD");   // 2999
fromMinorUnit(2999, "USD");  // 29.99

On this page