Every collection in your store — standard models, custom models, and app-defined models alike — supports a rich query language through the Backend API. This guide covers the query features that go beyond basic listing: filtering, expanding linked records, including related data in a single request, aggregations, and search. Examples use the swell-node client, and the same parameters work as query strings on direct HTTP requests.

The where parameter filters records using MongoDB-style query operators such as $eq, $ne, $gt, $in, and $or, including on nested fields with dot notation.

// Active products over $50
await swell.get('/products', {
  where: {
    active: true,
    price: { $gt: 50 },
  },
});

// Orders that are either on hold or unpaid
await swell.get('/orders', {
  where: {
    $or: [{ hold: true }, { paid: false }],
  },
});

Use sort with a field name and direction, limit to control page size (up to 1,000 records per request), and page to paginate through results.

await swell.get('/orders', {
  where: { paid: true },
  sort: 'date_created desc',
  limit: 50,
  page: 2,
});

Most models reference other models through link fields — an order links to its account, a review links to its product. The expand parameter resolves those links in the same request. Expand multiple links with a comma-separated list, and nest through links with dot notation (up to 5 levels deep).

// Expand the order's account, and each item's product
await swell.get('/orders', {
  expand: 'account, items.product',
});

When the expanded link is a collection, Swell returns up to 5 records by default. Specify a different count with a colon:

// Expand up to 20 variants on the product
await swell.get('/products/{id}', {
  id: '5f63e175e7eed80c11766c83',
  expand: 'variants:20',
});

While expand follows link fields defined on the model, include attaches the result of an arbitrary sub-query to each record in the response — any endpoint, with its own query parameters. Each include has a url to request, optional params that map fields from the parent record into the sub-query, and optional data with fixed query parameters.

// List accounts, each with their most recent order attached
await swell.get('/accounts', {
  limit: 25,
  include: {
    last_order: {
      url: '/orders/:last',
      params: {
        account_id: 'id',
      },
      data: {
        fields: 'number, grand_total, date_created',
      },
    },
  },
});

In this example, params: { account_id: 'id' } sets each sub-query's account_id filter to the parent account's id. The result is attached to each record under the include's key — here, last_order. An include can also define conditions evaluated against the parent record, so the sub-query only runs when the criteria match.

Every collection supports a set of special endpoints for aggregate results:

  • /:count — the number of records matching a query.
  • /:first and /:last — the first or most recent matching record.
  • /:group — aggregate values across matching records, using MongoDB-style accumulators such as $sum.
// How many paid orders does this customer have?
const count = await swell.get('/orders/:count', {
  account_id: accountId,
  canceled: { $ne: true },
});

// The customer's most recent order
const lastOrder = await swell.get('/orders/:last', {
  account_id: accountId,
});

// Total sales and order count for paid orders
const stats = await swell.get('/orders/:group', {
  where: { paid: true },
  count: { $sum: 1 },
  total_sales: { $sum: 'grand_total' },
});

With /:group, each key in the query besides where becomes a field in the result — the example above returns an object like { count: 152, total_sales: 8940.5 }. Note that for /:count, /:first, and /:last, top-level query fields act as the filter directly.

The search parameter matches a term across a model's searchable fields, and combines with where filters.

await swell.get('/products', {
  search: 'organic cotton',
  where: { active: true },
});

For performance — especially inside app functions and includes — use the fields parameter to return only what you need.

await swell.get('/products', {
  fields: 'name, slug, price',
  limit: 100,
});

These parameters work on every collection, including custom and app-defined models. For per-model fields and endpoints, see the Backend API reference. For querying from a storefront with public keys, see the Frontend API, which supports a compatible query syntax limited by public permissions.