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-jsSetup
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 SDKcommerce.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) // numbercommerce.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