# Basic usage

Source: https://developers.swell.is/frontend-api/basic-usage

**Swell.js:**

Once the client is [initialized](https://developers.swell.is/frontend-api/setup), 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**

**swell.js**

```javascript
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](https://developers.swell.is/frontend-api/setup) — it converts the keys you send as well as the ones you get back.

### Synchronous helpers

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.js**

```javascript
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 queries

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**

**swell.js** `Query`

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

**swell.js** `Response`

```json
{
  "count": 112,
  "page": 1,
  "page_count": 5,
  "limit": 25,
  "results": [
    {
      "id": "5c15505200c7d14d851e510f",
      "name": "Longsword",
      "price": 50
    }
  ]
}
```

### Query operators

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](https://developers.swell.is/backend-api/querying/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**

**swell.js** `Query`

```javascript
// 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' }
  }
})
```

### Handling errors

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**

**swell.js**

```javascript
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)
}
```

### Sessions

Swell.js keeps a [session](https://developers.swell.is/frontend-api/session-management) 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**

**swell.js** `Per request`

```javascript
// 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()
```

**swell.js** `Cookie handlers`

```javascript
// Use create() rather than init() on a server: init() configures the
// shared client, so concurrent requests would overwrite each other
const client = swell.create('my-store', 'pk_...', {
  getCookie: (name) => req.cookies[name],
  setCookie: (name, value) => res.cookie(name, value)
})
```

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.

### React Native and Expo

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**

**swell.js** `Query`

```javascript
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.

### Calling an endpoint directly

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**

**swell.js**

```javascript
// 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')
```

**GraphQL:**

Say our good friend Urbul gro-Orkulg wanted to check the products in his cart and to add some to the list.

#### Fetching cart items

He could see if there are already any items in his cart by using the following query:

**Fetch cart items**

**GraphQL** `Query`

```graphql
# Request header
# "Authorization": "pk_xXniL6sr8MVpjAZgRgPaPaZ2d4W93tNY"

query getCart {
  cart {
    items {
      id
      quantity
      price
      product {
        id
        name
        currency
      }
    }
  }
}
```

**GraphQL** `Response`

```graphql
# Response header
# "x-session": "abc123..."

{
   "data": {
    "cart": {
      "items": null
    }
  }
}
```

Since no session token was provided, a new session is generated with a corresponding token and an empty cart.

> **Tip:** When making an initial query, a session is generated automatically with the first operation for use with subsequent queries.

> **Warning:** If attempting to fetch an existing cart, then the session token should be included within the request. Otherwise, it will be assumed that the intention is to create a new session.

#### Adding items to the cart

Urbul is surprised to see the cart empty. He’s completely unprepared for battle. He could use the following mutation to add some items to this session’s cart:

**Add some items to the cart**

**GraphQL** `Mutation`

```graphql
# Request header
# "Authorization": "pk_xXniL6sr8MVpjAZgRgPaPaZ2d4W93tNY"

mutation addToCart {
  addCartItem(input: { productId: "6298d900f97f61001253d90f", quantity: 2 }) {
    items {
      id
      quantity
      price
      product {
        id
        name
        currency
      }
    }
  }
}
```

**GraphQL** `Response`

```graphql
# Response header
# "x-session": "abc123..."

{
  "data": {
    "addCartItem": {
      "items": [
        {
          "id": "6356f132b90c450012a71e49",
          "quantity": 2,
          "price": 20,
          "product": {
            "id": "6298d900f97f61001253d90f",
            "name": "Longsword",
            "currency": "USD"
          }
        }
      ]
    }
  }
}
```

By specifying a session token within the HTTP Headers section prior to making additional queries, he can make sure that he never loses track of his cart’s items.

#### Query

Finally, he can try fetching the cart data from his current session again with the following query:

**Checking his cart**

**GraphQL** `Query`

```graphql
# Request header
# "Authorization": "pk_xXniL6sr8MVpjAZgRgPaPaZ2d4W93tNY"

query getCart {
  cart {
    items {
      id
      quantity
      price
      product {
        id
        name
        currency
      }
    }
  }
}
```

**GraphQL** `Response`

```graphql
# Response header
# "x-session": "abc123..."

{
  "data": {
    "cart": {
      "items": [
        {
          "id": "6356f2ef93ea970013edfbd5",
          "quantity": 2,
          "price": 20,
          "product": {
            "id": "6298d900f97f61001253d90f",
            "name": "Longsword",
            "currency": "USD"
          }
        }
      ]
    }
  }
}
```

Success! He would find that his wares were added to the cart successfully. Time to go find some Daedra!
