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
where
predicate with strategic query parameters.Step 1: Choose the right endpoint
Endpoint | When to use |
---|---|
Project-scoped: /carts | Use 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}/carts | Use 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. |
/carts
endpoint. It provides the most flexibility.Step 2: Build the where predicate
where
predicate is the most critical part of your query. For best performance, order the clauses from most to least selective.Predicate component | Purpose |
---|---|
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. |
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.
Parameter | Recommended value | Purpose |
---|---|---|
sort | lastModifiedAt desc | Ensures 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. |
limit | 1 | Enforces 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. |
withTotal | false | Improves 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
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
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:
- Remove
limit=1
: allow the query to return all active Carts. - Use
sort
: ensure results are consistently ordered (for example,lastModifiedAt desc
). - Implement selection logic: your application must provide a way for the user to view and select which Cart they want to interact with.
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
- Define a Custom Type: create a Custom Type for Carts (for example,
cart-attributes
) with a field likecartType
(of typeEnum
orString
). - Set the Custom Field value on creation: when creating a Cart, populate the Custom Field.
const cartDraft: CartDraft = {
customerId: customerId,
currency: "USD",
country: "US",
custom: {
type: { key: "cart-attributes", typeId: "type" },
fields: {
cartType: "B2B-Project-A",
},
},
};
// ... post the cartDraft
- Query by Custom Field: add the custom field to your
where
predicate to find specific types of Carts.
const where = [
`customerId="${customerId}"`,
`cartState="Active"`,
`custom(fields(cartType="B2B-Project-A"))`,
];