Find and manage existing Carts

Discover efficient methods for retrieving and managing customer Carts.

After completing this page, you should be able to:

  • Construct an optimized API query to retrieve a customer's Cart, selecting the correct endpoint and parameters for the given context.
  • Implement SDK logic to find and manage existing Carts for both anonymous and registered customers.

Before creating a new Cart, it is a crucial best practice to check if the customer (anonymous or logged-in) already has an active one. This prevents duplicate Carts, reduces data clutter, and allows customers to seamlessly resume their shopping session. Most commerce sites enforce a "one active Cart per customer" rule to simplify the user experience.

Construct the optimal query

To find the correct Cart efficiently, you must build a precise and performant query. This involves choosing the right endpoint and combining a where predicate with strategic query parameters.

Step 1: Choose the right endpoint

EndpointWhen to use
Project-scoped: /cartsUse this if your application does not use Stores, or if you need to find a customer's Cart without knowing which Store it might belong to. It offers maximum flexibility, as you can optionally filter by store using a where predicate.
Store-scoped: /in-store/key={storeKey}/cartsUse this if your application logic is always confined to a single, known Store context. It is often more performant as the Store filter is applied at the infrastructure level. It's also ideal when using an API Client with permissions restricted to a single Store.
Recommendation: when in doubt, start with the Project-scoped /carts endpoint. It provides the most flexibility.

Step 2: Build the where predicate

The where predicate is the most critical part of your query. For best performance, order the clauses from most to least selective.
Predicate componentPurpose
customerId="\<id\>" or anonymousId="\<id\>"Identify the customer. This is the primary and most selective filter. Use customerId for logged-in customers and anonymousId for guests.
cartState in ("Active", "Frozen")Filter by Cart State. This excludes Carts that are no longer modifiable (Ordered, Merged), ensuring you only retrieve Carts relevant to an ongoing shopping session.
store(key="\<store-key\>")Isolate by Store. (Optional) If using the /carts endpoint in a multi-store setup, this filter ensures you get the Cart for the correct operational context. This is not needed if using the /in-store/... endpoint.
The following is an example where predicate:
where=customerId="c123-..." AND cartState in ("Active", "Frozen") AND store(key="zen-electron-us")

Step 3: Add performance and consistency parameters

Use the following URL query parameters to refine the request and improve performance.

ParameterRecommended valuePurpose
sortlastModifiedAt descEnsures consistency. When combined with limit=1, this guarantees you retrieve the most recently updated Cart, which is vital if a customer somehow has multiple active Carts.
limit1Enforces business logic and performance. If your rule is "one active Cart per customer," this enforces it. It also improves performance by allowing the API to stop searching after finding the first match.
withTotalfalseImproves performance. When you only need one result, calculating the total number of matching documents is unnecessary overhead. Setting this to false can significantly speed up the API response.

Complete API request example

Scenario: find the most recently modified active or frozen Cart for a logged-in customer in the zen-electron-us Store:
GET /carts?limit=1&withTotal=false&sort=lastModifiedAt desc&where=customerId="c123-..." AND cartState in ("Active", "Frozen") AND store(key="zen-electron-us")

Handling the response:

  • If a Cart is found: the API will return a results array with one Cart object. Your application should use this Cart.
  • If no Cart is found: the API will return an empty results array. Your application should proceed to create a new Cart when the user adds their first item.

Implementation with the SDK

Here is how you can implement this optimal query strategy using the TypeScript SDK.

For an anonymous user:

import { apiRoot } from "../../client";
import { Cart } from "@Composable Commerce/platform-sdk";

async function getActiveAnonymousCart(
  storeKey: string,
  anonymousId: string
): Promise<Cart | undefined> {
  try {
    const { body } = await apiRoot
      .inStoreKeyWithStoreKeyValue({ storeKey })
      .carts()
      .get({
        queryArgs: {
          limit: 1,
          withTotal: false,
          sort: "lastModifiedAt desc",
          where: [`anonymousId="${anonymousId}"`, `cartState="Active"`],
        },
      })
      .execute();

    return body.results[0]; // Returns the cart or undefined if not found
  } catch (error) {
    console.error("Error fetching anonymous cart:", error);
    throw error; // Rethrow or handle appropriately
  }
}

For a logged-in customer:

import { apiRoot } from "../../client";
import { Cart } from "@Composable Commerce/platform-sdk";

async function getActiveCustomerCart(
  storeKey: string,
  customerId: string
): Promise<Cart | undefined> {
  try {
    const { body } = await apiRoot
      .inStoreKeyWithStoreKeyValue({ storeKey })
      .carts()
      .get({
        queryArgs: {
          limit: 1,
          withTotal: false,
          sort: "lastModifiedAt desc",
          where: [`customerId="${customerId}"`, `cartState="Active"`],
        },
      })
      .execute();

    return body.results[0]; // Returns the cart or undefined if not found
  } catch (error) {
    console.error("Error fetching customer cart:", error);
    throw error; // Rethrow or handle appropriately
  }
}

How to enforce a single active Cart

The primary way to enforce this is through your application logic. By always performing the optimal query described above (limit=1) before creating a new Cart, you ensure that you always reuse an existing active Cart if one is found.

How to handle multiple Carts

While a single active Cart is the most common pattern, some business models might require multiple active Carts per user (for example, a B2B "projects" Cart and a "personal" Cart).

If your business logic allows multiple active Carts, modify your query strategy:

  1. Remove limit=1: allow the query to return all active Carts.
  2. Use sort: ensure results are consistently ordered (for example, lastModifiedAt desc).
  3. Implement selection logic: your application must provide a way for the user to view and select which Cart they want to interact with.
Example of getting all active Carts for a customertypescript
async function getAllActiveCustomerCarts(
  storeKey: string,
  customerId: string
): Promise<Cart[]> {
  try {
    const { body } = await apiRoot
      .inStoreKeyWithStoreKeyValue({ storeKey })
      .carts()
      .get({
        queryArgs: {
          sort: "lastModifiedAt desc",
          where: [`customerId="${customerId}"`, `cartState="Active"`],
        },
      })
      .execute();

    return body.results; // Returns an array of all active carts
  } catch (error) {
    console.error("Error fetching all customer carts:", error);
    return [];
  }
}

Use Custom Fields to differentiate Carts

In a multi-Cart scenario, consider Custom Fields to categorize Carts. In some B2B cases you might allow the customer to give each Cart a unique name, so that they can easily identify what each Cart is for.
  1. Define a Custom Type: create a Custom Type for Carts (for example, cart-attributes) with a field like cartType (of type Enum or String).
  2. Set the Custom Field value on creation: when creating a Cart, populate the Custom Field.
Example of creating a Card with a Custom Typetypescript

const cartDraft: CartDraft = {
  customerId: customerId,
  currency: "USD",
  country: "US",
  custom: {
    type: { key: "cart-attributes", typeId: "type" },
    fields: {
      cartType: "B2B-Project-A",
    },
  },
};
// ... post the cartDraft
  1. Query by Custom Field: add the custom field to your where predicate to find specific types of Carts.
Example of querying for a specific Cart typetypescript

const where = [
  `customerId="${customerId}"`,
  `cartState="Active"`,
  `custom(fields(cartType="B2B-Project-A"))`,
];

Test your knowledge