As a best practice, a Cart should only be created when a user performs an action that requires one, such as adding their first item. Creating Carts for every visitor who lands on your site leads to unnecessary data and can impact performance.
Therefore, the standard workflow is:
- When a user tries to add an item, first check for an existing
ActiveorFrozenCart. - If the query returns no results, proceed to create a new Cart. Alternatively, you can check for an existing Cart after login or at another stage prior to attempting to create a new Cart.
To provide practical examples, we'll use the scenario of Zen Electron, a company that operates two distinct brands from a single Project, using Stores for separation:
| Brand | Store Key | Description |
|---|---|---|
| Electronics High Tech | electronics-high-tech-store | A budget-friendly electronics retailer. |
| Zenith Living | zenith-living-store | A high-end home appliance brand focused on service. |
This multi-store setup will inform how we construct our API requests.
The CartDraft: your Cart's blueprint
CartDraft object. This object serves as a template, defining the initial properties of the Cart. Properly configuring the CartDraft is essential for ensuring correct price selection, tax calculation, and user association.| Field | Requirement | Purpose and usage |
|---|---|---|
currency | Mandatory | Defines the currency for all prices in the Cart, for example, USD or EUR. This is a primary factor in Price Selection. |
customerId or anonymousId | Mandatory | Associates the Cart with a user. Use customerId for logged-in Customers and anonymousId for guest users. |
country | Recommended | A two-letter country code, for example, US or DE. It influences price selection, tax calculation, and available shipping methods. |
store | Recommended if you use Stores | A ResourceIdentifier to a Store. It associates the Cart with a specific Store, inheriting its product assortment, pricing, and other contextual settings. |
lineItems | Optional | An array of LineItemDraft objects to add products from your catalog when you create the Cart. You must provide a sku or a productId and variantId. |
locale | Recommended | A language tag, for example, en-US. It ensures that localized fields such as product names in LineItem.name are returned in the customer's preferred language in the API response. |
customerGroup | Optional | A ResourceIdentifier to a Customer Group. If the logged-in Customer belongs to a Customer Group, the system sets this value automatically. You can also set it for guest users. |
shippingAddress | Optional | The initial shipping address for the Cart. You can also set it later with an update action. |
taxMode | Optional | Defines how taxes are handled. It defaults to Platform. For external tax services, set it to External or ExternalAmount. For details, see Tax modes. |
custom | Optional | Extends the Cart with custom business logic using a Custom Type, for example, to flag a Cart as a gift order or store an internal identifier. |
key | Optional | A user-defined unique identifier. It is useful for integrations with external systems that need a stable, human-readable key to reference the Cart. This field supports idempotency. |
CartDraft schema and every optional field, see the CartDraft reference.Best practice: optimize Cart creation for performance
CartDraft is designed to be comprehensive, allowing you to set multiple Cart properties in a single API request. For optimal performance, you should bundle as much information as possible into the initial creation call.lineItems and shippingAddress directly in the CartDraft. This approach reduces the number of API requests, leading to a faster and more efficient user experience.Create a Cart with the SDK
The following functions demonstrate how to create a new Cart for anonymous and logged-in customers, including the first Line Item.
import { apiRoot } from "../ctp-root";
import {
CartDraft,
Cart,
LineItemDraft,
} from "@commercetools/platform-sdk";
/**
* Creates a new Cart for an anonymous user within a specific Store, adding an initial Line Item.
* @param storeKey The key of the Store the Cart should belong to.
* @param anonymousId The anonymous ID to associate with the Cart.
* @param currency The currency for the Cart (for example, "AUD", "NZD").
* @param country The country for the Cart (for example, "AU", "NZ").
* @param sku SKU of the product variant to add.
* @param quantity Quantity of the product variant to add.
* @returns A Promise resolving to the newly created Cart.
*/
async function createAnonymousCart(
storeKey: string,
anonymousId: string,
currency: string,
country: string,
sku: string,
quantity: number
): Promise<Cart> {
const cartDraft: CartDraft = {
currency,
country,
anonymousId, // Link the cart to the guest session
lineItems: [
{ sku, quantity }, // Add the initial item
],
};
const { body } = await apiRoot
.inStoreKeyWithStoreKeyValue({ storeKey }) // Use the Store-scoped endpoint
.carts()
.post({
body: cartDraft,
})
.execute();
return body;
}
/**
* Creates a new Cart for a signed-in Customer within a specific Store, adding an initial Line Item.
* @param storeKey The key of the Store the Cart should belong to.
* @param customerId The ID of the customer to associate with the Cart.
* @param currency The currency for the Cart.
* @param country The country for the Cart.
* @param sku SKU of the product variant to add.
* @param quantity Quantity of the product variant to add.
* @returns A Promise resolving to the newly created Cart.
*/
async function createCustomerCart(
storeKey: string,
customerId: string,
currency: string,
country: string,
sku: string,
quantity: number
): Promise<Cart> {
const cartDraft: CartDraft = {
currency,
country,
customerId, // Link the cart to the customer account
lineItems: [{ sku, quantity }],
};
const { body } = await apiRoot
.inStoreKeyWithStoreKeyValue({ storeKey })
.carts()
.post({
body: cartDraft,
})
.execute();
return body;
}
CartDraft includes the mandatory currency and country fields, which are critical for price selection. The Cart is linked to a customer using either their anonymousId or customerId, and the request is sent to the appropriate Store-scoped endpoint.currency, country, customerGroup, and channel, the API will return a 400 Bad Request error with the code MatchingPriceNotFound. Ensure your product data includes prices that match the contexts your customers will be shopping in.