Once the client is initialized, most methods live under a namespace named for the resource they work with — swell.products, swell.cart, swell.account, and so on — and those methods return a promise. A few namespaces are helpers rather than resources (swell.currency, swell.locale, swell.settings, swell.card), and the generic HTTP methods sit at the top level. The examples here use async/await, but you can use promises directly.

Basic usage
import swell from 'swell-js'

// Initialize the client first
swell.init('my-store', 'pk_md0JkpLnp9gBjkQ085oiebb0XBuwqZX9')

// Now you can use any method
await swell.products.list({
  category: 't-shirts',
  limit: 25,
  page: 1
})

Responses use the API's snake_case field names by default. To work with camelCase keys instead, initialize the client with the useCamelCase option — it converts the keys you send as well as the ones you get back.

Not every method makes a request. The local helpers return a value directly: currency and locale formatting and the selection readers such as selected() and list(), the product helpers such as variation() and filters(), the card validators, and session.getCookie(). Selecting a currency or locale is not local — currency.select() and locale.select() update the session and cart, so they return a promise.

Settings are the exception worth knowing about. The state-backed getters — get(), menus(), payments(), subscriptions() — return a promise until their first request resolves, and a plain value afterward, so always await them. Better, call swell.settings.load() once at startup, which fetches all of them in a single request.

Load settings at startup
swell.init('my-store', 'pk_md0JkpLnp9gBjkQ085oiebb0XBuwqZX9')

// Fetch all store settings in a single request
await swell.settings.load()

// The local helpers are now safe to call synchronously
swell.currency.format(19.99) // => $19.99
swell.currency.format(1234.5) // => $1,234.50

List methods take a query object that's passed to the API as-is: limit and page for offset pagination (the default limit is 15 and the maximum is 1000), where for filtering, sort for ordering (the default is id desc), search for full-text search, and expand to include linked records. Any other key you pass is forwarded too, and the API folds it into the where filter.

A list resolves to an object with count, page, limit, and results. Methods that fetch a single record return that record on its own, or null when there's nothing to return — a slug that matches no record, a session with no cart yet, or no account logged in.

List queries
await swell.products.list({
  limit: 25,
  page: 1,
  where: { active: true },
  sort: 'price asc',
  expand: ['variants']
})

Swell.js reports failures in two different ways, and code that writes to a cart or an account should handle both.

Validation errors resolve rather than throw. Cart, account, and payment methods return the response with an errors object on it — keyed by field path, or by a source such as gateway — where each value has a code and a message. Check for it before treating the result as a success.

Most other failures reject. An invalid key or a server error rejects the promise with an Error carrying message, status, code, and param, while a network failure rejects with the browser's own TypeError, which has none of them — so don't rely on those properties being present. Two methods behave differently: settings.load() logs the error and resolves anyway rather than rejecting, and products.variation() throws synchronously when the requested purchase option isn't active.

Handling errors
try {
  const cart = await swell.cart.addItem({
    product_id: '5c15505200c7d14d851e510f',
    quantity: 1
  })

  if (cart.errors) {
    for (const [field, error] of Object.entries(cart.errors)) {
      console.log(field, error.message)
    }
    return
  }

  // cart is the updated cart
} catch (err) {
  console.log(err.message, err.status, err.code)
}

Swell.js keeps a session for each visitor, and that session is what identifies their cart — there's no cart ID to keep track of. In the browser this is automatic: the API returns a token in an X-Session response header, the library stores it in a swell-session cookie, and sends it with later requests.

On a server there's no cookie store, so unless you supply a session every request starts a new one — and therefore an empty cart. Either create a client per request with the visitor's token, or connect the library's cookie handlers to your framework. Use swell.create() for per-request clients rather than the shared one, so one visitor's session can't leak into another's request.

Sessions on the server
// Pass the visitor's session token from your own request handling
const client = swell.create('my-store', 'pk_...', {
  session: sessionToken
})

const cart = await client.cart.get()

These options are cookie accessors for your server framework, and are unrelated to swell.session.getCookie() and swell.session.setCookie(), which read and restore the encoded session string. Note that swell.cache is shared across every client in the same process, so clear it between requests if one process serves more than one store.

For any Frontend API endpoint without a dedicated method, use swell.get(), swell.put(), swell.post(), or swell.delete(). These use the public key like every other Swell.js call — they aren't the swell-node Backend API client, which authenticates with a secret key and must never run in a browser.

For get() the second argument is a query object; for put(), post(), and delete() it's the request body. Passing a string instead appends it to the URL as a path segment, which is how you fetch a single record — on a write that replaces the body, so use swell.request(method, url, id, data) when you need both.

Calling an endpoint directly
// Paths are relative to https://{store}.swell.store/api

// GET /api/settings/menus
await swell.get('/settings/menus')

// GET /api/products?limit=5
await swell.get('/products', { limit: 5 })

// GET /api/products/blue-shoes
await swell.get('/products', 'blue-shoes')