Skip to main content

Customize a cart handler

The cart handler is an interface that allows you to easily make cart query and mutation requests to the Storefront API. It creates a new cart if one doesn't already exist, and provides default built-in cart queries.

This guide shows you how to customize a cart handler. You can customize a cart handler to return different cart query fields, override existing cart query functions, and add new cart query functions.



Anchor to Customize the return cart fieldsCustomize the return cart fields

The createCartHandler includes a default query fragment. Pass a custom cartQueryFragment if you need additional data from the Storefront API. See the default CartApiQuery fragment for reference. In this example, we added the note field.

File

server.js

const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
cartQueryFragment: CART_QUERY_FRAGMENT, // Your custom cart query fragment
});

/**
* cartQueryFragment requirements:
*
* - Must be named `CartApiQuery`
* - Only have access to the following query variables:
* - $cartId: ID!
* - $country: CountryCode
* - $language: LanguageCode
* - $numCartLines: Int
**/
const CART_QUERY_FRAGMENT = `#graphql
fragment CartApiQuery on Cart {
note
id
checkoutUrl
totalQuantity
buyerIdentity {
countryCode
customer {
id
email
firstName
lastName
displayName
}
email
phone
}
lines(first: $numCartLines) {
edges {
node {
id
quantity
attributes {
key
value
}
cost {
totalAmount {
amount
currencyCode
}
amountPerQuantity {
amount
currencyCode
}
compareAtAmountPerQuantity {
amount
currencyCode
}
}
merchandise {
... on ProductVariant {
id
availableForSale
compareAtPrice {
...CartApiMoney
}
price {
...CartApiMoney
}
requiresShipping
title
image {
...CartApiImage
}
product {
handle
title
id
}
selectedOptions {
name
value
}
}
}
}
}
}
cost {
subtotalAmount {
...CartApiMoney
}
totalAmount {
...CartApiMoney
}
totalDutyAmount {
...CartApiMoney
}
totalTaxAmount {
...CartApiMoney
}
}
note
attributes {
key
value
}
discountCodes {
applicable
code
}
}

fragment CartApiMoney on MoneyV2 {
currencyCode
amount
}

fragment CartApiImage on Image {
id
url
altText
width
height
}
`;
const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
cartQueryFragment: CART_QUERY_FRAGMENT, // Your custom cart query fragment
});

/**
* cartQueryFragment requirements:
*
* - Must be named `CartApiQuery`
* - Only have access to the following query variables:
* - $cartId: ID!
* - $country: CountryCode
* - $language: LanguageCode
* - $numCartLines: Int
**/
const CART_QUERY_FRAGMENT = `#graphql
fragment CartApiQuery on Cart {
note
id
checkoutUrl
totalQuantity
buyerIdentity {
countryCode
customer {
id
email
firstName
lastName
displayName
}
email
phone
}
lines(first: $numCartLines) {
edges {
node {
id
quantity
attributes {
key
value
}
cost {
totalAmount {
amount
currencyCode
}
amountPerQuantity {
amount
currencyCode
}
compareAtAmountPerQuantity {
amount
currencyCode
}
}
merchandise {
... on ProductVariant {
id
availableForSale
compareAtPrice {
...CartApiMoney
}
price {
...CartApiMoney
}
requiresShipping
title
image {
...CartApiImage
}
product {
handle
title
id
}
selectedOptions {
name
value
}
}
}
}
}
}
cost {
subtotalAmount {
...CartApiMoney
}
totalAmount {
...CartApiMoney
}
totalDutyAmount {
...CartApiMoney
}
totalTaxAmount {
...CartApiMoney
}
}
note
attributes {
key
value
}
discountCodes {
applicable
code
}
}

fragment CartApiMoney on MoneyV2 {
currencyCode
amount
}

fragment CartApiImage on Image {
id
url
altText
width
height
}
`;

Provide cartMutateFragment to customize what data is returned from mutation queries. See the default CartApiMutation fragment for reference. In this example, we added the checkoutUrl field.

File

server.js

const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
cartMutateFragment: CART_MUTATE_FRAGMENT, // Your custom cart mutate fragment
});

/**
* cartMutateFragment requirements:
*
* - Must be named `CartApiMutation`
* - Only have access to the following query variables:
* - $cartId: ID!
* - $country: CountryCode
* - $language: LanguageCode
**/
const CART_MUTATE_FRAGMENT = `#graphql
fragment CartApiMutation on Cart {
id
totalQuantity
checkoutUrl
}
`;
const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
cartMutateFragment: CART_MUTATE_FRAGMENT, // Your custom cart mutate fragment
});

/**
* cartMutateFragment requirements:
*
* - Must be named `CartApiMutation`
* - Only have access to the following query variables:
* - $cartId: ID!
* - $country: CountryCode
* - $language: LanguageCode
**/
const CART_MUTATE_FRAGMENT = `#graphql
fragment CartApiMutation on Cart {
id
totalQuantity
checkoutUrl
}
`;

Anchor to Add custom methods to cart handlerAdd custom methods to cart handler

If you have common cart operations that you would like to reuse across your app, you can add custom methods to your cart handler. For example, add and remove an item from the cart at the same time.

File

server.js

const cartQueryOptions = {
storefront,
getCartId: cartGetIdDefault(request.headers),
};

const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
customMethods: {
editInLine: async (addLines, removeLineIds, optionalParams) => {
// Using Hydrogen default cart query methods

// Add line items
await cartLinesAddDefault(cartQueryOptions)(addLines, optionalParams);

// Remove line items
return await cartLinesRemoveDefault(cartQueryOptions)(
removeLineIds,
optionalParams,
);
}
},
});
import {type CartQueryOptions} from '@shopify/hydrogen';

const cartQueryOptions: CartQueryOptions = {
storefront,
getCartId: cartGetIdDefault(request.headers),
};

const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
customMethods: {
editInLine: async (addLines, removeLineIds, optionalParams) => {
// Using Hydrogen default cart query methods

// Add line items
await cartLinesAddDefault(cartQueryOptions)(addLines, optionalParams);

// Remove line items
return await cartLinesRemoveDefault(cartQueryOptions)(
removeLineIds,
optionalParams,
);
}
},
});

Anchor to Override existing cart handler methodsOverride existing cart handler methods

Hydrogen provides out of the box multiple methods for interacting with the cart. You can override any default method with your own logic. For example, you can override the updateAttributes to do something custom with cart attributes:

The default cart handler includes basic cart logic. If you override these methods, you need to implement the cart logic as well. For example when adding a line item to a cart, you would need to implement the logic needed to create a cart with the line item.

The following methods include create cart logic::

  • addLines - If a cart doesn't exist, then create a cart with the line items.
  • setMetafields - If a cart doesn't exist, then create a cart with the metafields applied.
  • updateAttributes - If a cart doesn't exist, then create a cart with the attributes applied.
  • updateBuyerIdentity - If a cart doesn't exist, then create a cart with the buyer identity applied.
  • updateDiscountCode - If a cart doesn't exist, then create a cart with the discount applied.
  • updateNote - If a cart doesn't exist, then create a cart with the note applied.

File

server.js

const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
customMethods: {
updateAttributes: async (attributes, optionalParams) => {

// Do something with attributes

// Make a mutation query to update cart attributes
const {cartAttributesUpdate} = await options.storefront.mutate(CART_ATTRIBUTES_UPDATE_MUTATION, {
variables: {
cartId: optionalParams?.cartId || options.getCartId(),
attributes,
},
});
return cartAttributesUpdate;
},
},
});

export const CART_ATTRIBUTES_UPDATE_MUTATION = `#graphql
mutation cartAttributesUpdate(
$cartId: ID!
$attributes: [CartAttributeInput!]!
) {
cartAttributesUpdate(cartId: $cartId, attributes: $attributes) {
cart {
id
}
}
}
`;
const cart = createCartHandler({
storefront,
getCartId: cartGetIdDefault(request.headers),
setCartId: cartSetIdDefault(),
customMethods: {
updateAttributes: async (attributes, optionalParams) => {

// Do something with attributes

// Make a mutation query to update cart attributes
const {cartAttributesUpdate} = await options.storefront.mutate<{
cartAttributesUpdate: CartQueryData;
}>(CART_ATTRIBUTES_UPDATE_MUTATION, {
variables: {
cartId: optionalParams?.cartId || options.getCartId(),
attributes,
},
});
return cartAttributesUpdate;
},
},
});

export const CART_ATTRIBUTES_UPDATE_MUTATION = `#graphql
mutation cartAttributesUpdate(
$cartId: ID!
$attributes: [CartAttributeInput!]!
) {
cartAttributesUpdate(cartId: $cartId, attributes: $attributes) {
cart {
id
}
}
}
`;

Was this page helpful?