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, page_count, 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']
})

The where object accepts the same query operators as the Backend API. Comparison operators such as $gt, $lt, $in and $regex apply to a single field. Logical operators such as $or, $and and $nor take an array of conditions. See Filtering for the full list.

Multiple fields in one where object are combined with $and, and so are multiple operators on the same field, so $and only needs to be written out when a query also uses another logical operator.

Query operators
// Products under $20 or over $100
await swell.products.list({
  where: {
    $or: [
      { price: { $lt: 20 } },
      { price: { $gt: 100 } }
    ]
  }
})

// Products between $20 and $100
await swell.products.list({
  where: {
    price: { $gt: 20, $lt: 100 }
  }
})

// Products with a material attribute of 'Silver' or 'Gold'
await swell.products.list({
  where: {
    'attributes.material': { $in: ['Silver', 'Gold'] }
  }
})

// Products with 'shoes' in the name, ignoring case
await swell.products.list({
  where: {
    name: { $regex: 'shoes', $options: 'i' }
  }
})

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.

Swell.js runs in React Native, including Expo, with two differences from a browser.

Sessions are not stored for you. The library keeps the session in a cookie, and React Native has no cookie store, so every request would otherwise start a new session and therefore an empty cart. Pass your own getCookie and setCookie functions when you initialize the client, backed by whatever storage the app already uses, such as AsyncStorage or expo-secure-store. These are the same options described above for a server.

Initialize in React Native
import AsyncStorage from '@react-native-async-storage/async-storage';
import swell from 'swell-js';

const store = new Map();

// Load any saved values once at startup, then keep them in memory so the
// accessors below can stay synchronous.
for (const key of ['swell-session', 'swell-currency', 'swell-locale']) {
  const value = await AsyncStorage.getItem(key);
  if (value) store.set(key, value);
}

swell.init('my-store', 'pk_...', {
  getCookie: (name) => store.get(name),
  setCookie: (name, value) => {
    store.set(name, value);
    AsyncStorage.setItem(name, value);
  }
});

Payment elements are not available. Everything under swell.payment renders gateway-hosted fields into the page and loads the gateway's own script, both of which need a browser document, so card elements and the Apple Pay and Google Pay buttons cannot be used in a native app. Use each gateway's React Native SDK for those, and take payment on the resulting token.

The rest of the library works normally. Products, categories, carts, accounts, subscriptions, settings and content all use fetch, and so does swell.card.createToken(), so tokenizing raw card details is available even though the hosted elements are not.

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')