Swell offers native support for the MongoDB Aggregation Pipeline; a framework for data aggregation allowing you to summarize data for reporting and other purposes.

The simplest way to aggregate is the group parameter, accepted by every list endpoint. Combine it with where to filter records first. Each key of group either computes a value with an accumulator operator, buckets results with a date operator, or—when set to true—groups results by that field. Field names are prefixed with $ automatically.

  • Accumulators: $sum, $avg, $min, $max, $first, $last, $push, $addToSet
  • Date grouping: $year, $month, $dayOfMonth, $dayOfWeek, $dayOfYear, $hour, $minute, $second, $millisecond

Using only accumulators returns a single result object inside results:

// Total revenue and order count for paid orders
await swell.get('/orders', {
  where: { paid: true },
  group: {
    orders: { $sum: 1 },
    revenue: { $sum: 'grand_total' },
    avg_order: { $avg: 'grand_total' }
  }
});
{
  "results": [
    {
      "orders": 167,
      "revenue": 18962.75,
      "avg_order": 113.55
    }
  ]
}

Adding a date operator or a field key groups the results—the response then includes count, the number of groups returned. For example, monthly revenue:

// Revenue by month
await swell.get('/orders', {
  where: { paid: true },
  group: {
    orders: { $sum: 1 },
    revenue: { $sum: 'grand_total' },
    month: { $month: 'date_created' },
    year: { $year: 'date_created' }
  }
});

// Order count and total per customer
await swell.get('/orders', {
  group: {
    account_id: true,
    orders: { $sum: 1 },
    total: { $sum: 'grand_total' }
  }
});
{
  "count": 3,
  "results": [
    { "orders": 74, "revenue": 8214.50, "month": 1, "year": 2025 },
    { "orders": 92, "revenue": 10467.25, "month": 2, "year": 2025 },
    { "orders": 88, "revenue": 9806.00, "month": 3, "year": 2025 }
  ]
}

For anything beyond grouping—multi-stage pipelines, $unwind, $project, and so on—use the aggregate parameter to construct a pipeline according to this Mongo reference. See the example alongside this article. A few behaviors to know:

  • Passing an array replaces the entire pipeline—where filters are ignored, so include your own $match stage.
  • Passing an object of stages appends them after the $match stage generated from where.
  • Inside a pipeline, use MongoDB syntax directly, including $-prefixed field references such as $grand_total—unlike the group parameter, nothing is prefixed for you.
  • The response contains count and results, where count is the number of aggregated rows. Keys grouped under _id are flattened onto each result.
  • Inside a pipeline's $match, id is rewritten to _id and valid ID strings are converted to object IDs automatically.
  • Pagination is not applied to aggregations—limit and page are ignored, so cap and order results with your own $sort and $limit stages.
  • The fields, expand, and include parameters don't apply to aggregate results—use a $project stage to shape output. Each $lookup stage adds to the request's rate-limit weight.

The following pipelines answer common reporting questions using order data. Each includes its own $match stage, and uses $sort and $limit to keep results manageable.

Best-selling products—unwind order line items and group by product to find which products have sold the most units:

// Best-selling products by quantity
await swell.get('/orders', {
  aggregate: [
    { $match: { paid: true, canceled: { $ne: true } } },
    { $unwind: '$items' },
    {
      $group: {
        _id: { product_id: '$items.product_id' },
        product_name: { $first: '$items.product_name' },
        quantity_sold: { $sum: '$items.quantity' }
      }
    },
    { $sort: { quantity_sold: -1 } },
    { $limit: 3 }
  ]
});
{
  "count": 3,
  "results": [
    {
      "product_id": "5c15505ad2f52a4f6f2fd5f2",
      "product_name": "Organic Cotton Tee",
      "quantity_sold": 482
    },
    {
      "product_id": "5c3a80f7b281a12e6a3c8d91",
      "product_name": "Canvas Tote Bag",
      "quantity_sold": 317
    },
    {
      "product_id": "5d9e12c4a7b3f05e8c1d2e73",
      "product_name": "Enamel Camp Mug",
      "quantity_sold": 264
    }
  ]
}

Revenue by shipping country—group paid orders by the shipping address country:

// Revenue by shipping country
await swell.get('/orders', {
  aggregate: [
    { $match: { paid: true, canceled: { $ne: true } } },
    {
      $group: {
        _id: { country: '$shipping.country' },
        revenue: { $sum: '$grand_total' },
        orders: { $sum: 1 }
      }
    },
    { $sort: { revenue: -1 } }
  ]
});
{
  "count": 3,
  "results": [
    { "country": "US", "revenue": 148230.75, "orders": 1642 },
    { "country": "CA", "revenue": 32410.50, "orders": 388 },
    { "country": "GB", "revenue": 18754.20, "orders": 205 }
  ]
}

Top customers by lifetime value—sum every paid order per account:

// Top customers by lifetime value
await swell.get('/orders', {
  aggregate: [
    { $match: { paid: true, canceled: { $ne: true } } },
    {
      $group: {
        _id: { account_id: '$account_id' },
        lifetime_value: { $sum: '$grand_total' },
        order_count: { $sum: 1 }
      }
    },
    { $sort: { lifetime_value: -1 } },
    { $limit: 3 }
  ]
});
{
  "count": 3,
  "results": [
    {
      "account_id": "5b4e6a2fd3c1a94b2e7f8a10",
      "lifetime_value": 4821.40,
      "order_count": 27
    },
    {
      "account_id": "5c92d1e8b47f302a1d6c4b55",
      "lifetime_value": 3956.15,
      "order_count": 19
    },
    {
      "account_id": "5e07f3a9c25d816e4b9a2c38",
      "lifetime_value": 3110.00,
      "order_count": 22
    }
  ]
}

Average order value by coupon code—measure how each promotion affects basket size:

// Average order value by coupon code
await swell.get('/orders', {
  aggregate: [
    { $match: { paid: true, coupon_code: { $ne: null } } },
    {
      $group: {
        _id: { coupon_code: '$coupon_code' },
        avg_order_value: { $avg: '$grand_total' },
        orders: { $sum: 1 }
      }
    },
    { $sort: { avg_order_value: -1 } }
  ]
});
{
  "count": 3,
  "results": [
    { "coupon_code": "VIP20", "avg_order_value": 112.40, "orders": 96 },
    { "coupon_code": "SUMMER10", "avg_order_value": 84.75, "orders": 241 },
    { "coupon_code": "WELCOME5", "avg_order_value": 52.30, "orders": 418 }
  ]
}

For common aggregate results, every collection also supports the shortcut endpoints /:count, /:first, /:last, and /:group — see the Advanced queries guide for worked examples.

Unlike the group parameter, the /:group endpoint returns the aggregation result directly, without the count and results envelope.