Shopify Storefront APIでカートを操作する
Ajaxカートを実装するには、Shopify Storefront APIの「Cart」機能を使います。このページでは、実際にどのようにAPIを呼び出し、フロントエンドの状態と連携させるかを解説します。
使用する主なAPI(ミューテーション)
Shopify Storefront APIはGraphQLベースです。カート操作には以下の「ミューテーション」を使います。
| ミューテーション | 用途 |
|---|---|
| cartCreate | 新しいカートを作成 |
| cartLinesAdd | カートに商品を追加 |
| cartLinesUpdate | 商品の数量を変更 |
| cartLinesRemove | カートから商品を削除 |
| cartQuery | カートの中身を取得 |
全体の流れ
カート操作の基本的な流れは以下の通りです。
カートの作成(cartCreate)
お客様が初めてサイトに訪問したとき、まだカートが存在しません。最初の商品追加時、または初回訪問時にカートを作成します。
mutation cartCreate($input: CartInput!) {
cartCreate(input: $input) {
cart {
id
checkoutUrl
lines(first: 100) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
price {
amount
currencyCode
}
}
}
}
}
}
}
userErrors {
field
message
}
}
}
ポイント:
cart.idは後続のすべての操作で必要checkoutUrlは購入手続きへ進むときに使うuserErrorsでエラーをチェック
商品の追加(cartLinesAdd)
商品をカートに追加するときは cartLinesAdd を使います。
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
lines(first: 100) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
}
}
}
}
}
cost {
totalAmount {
amount
currencyCode
}
}
}
userErrors {
field
message
}
}
}
変数の例:
{
"cartId": "gid://shopify/Cart/abc123",
"lines": [
{
"merchandiseId": "gid://shopify/ProductVariant/12345",
"quantity": 1
}
]
}
数量の変更(cartLinesUpdate)
カート内の商品の数量を変更するには cartLinesUpdate を使います。
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
id
lines(first: 100) {
edges {
node {
id
quantity
}
}
}
cost {
totalAmount {
amount
currencyCode
}
}
}
userErrors {
field
message
}
}
}
ポイント:
lines[].idにはカートライン(商品行)のIDを指定- 商品バリアントのIDではなく、
cartLinesAddで返ってきたnode.idを使う
商品の削除(cartLinesRemove)
カートから商品を削除するには cartLinesRemove を使います。
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
cart {
id
lines(first: 100) {
edges {
node {
id
quantity
}
}
}
}
userErrors {
field
message
}
}
}
フロントエンドの状態管理
APIを呼び出すだけでなく、フロントエンド側でカートの状態を管理する必要があります。
React Contextを使う方法
アプリ全体でカート状態を共有するために、React Contextを使うのが一般的です。
// カートの状態を保持
interface CartState {
cartId: string | null;
lines: CartLine[];
totalAmount: string;
checkoutUrl: string;
isLoading: boolean;
}
// カートの操作を提供
interface CartActions {
addToCart: (variantId: string, quantity: number) => Promise<void>;
updateQuantity: (lineId: string, quantity: number) => Promise<void>;
removeFromCart: (lineId: string) => Promise<void>;
}
Zustandを使う方法
より軽量な状態管理ライブラリ「Zustand」を使う方法もあります。ボイラープレートが少なく、シンプルに書けます。
どちらを選ぶかはプロジェクトの規模や好みによりますが、カート状態がアプリ全体で一貫していることが重要です。
楽観的UI更新の実装
ユーザーがボタンを押してから、APIレスポンスが返ってくるまでには多少の時間がかかります。この間、何も変化がないとユーザーは不安になります。
楽観的UI更新では、APIリクエストを送ると同時に、UIを先に更新します。
async function addToCart(variantId: string, quantity: number) {
// 1. 即座にUIを更新(楽観的更新)
setCart(prev => ({
...prev,
lines: [...prev.lines, { variantId, quantity, pending: true }]
}));
try {
// 2. APIを呼び出し
const result = await cartLinesAdd(cartId, variantId, quantity);
// 3. 成功したら正式なデータで置き換え
setCart(prev => ({
...prev,
lines: result.cart.lines,
totalAmount: result.cart.cost.totalAmount.amount
}));
} catch (error) {
// 4. 失敗したら元に戻す
setCart(prev => ({
...prev,
lines: prev.lines.filter(line => !line.pending)
}));
showError("カートに追加できませんでした");
}
}
カートIDの永続化
ページをリロードしてもカートの中身が消えないように、カートIDをローカルストレージに保存します。
// カート作成後
localStorage.setItem('shopify_cart_id', cart.id);
// 再訪問時
const savedCartId = localStorage.getItem('shopify_cart_id');
if (savedCartId) {
const cart = await fetchCart(savedCartId);
setCart(cart);
}
注意点:
- カートには有効期限があります(Shopifyでは通常10日間)
- 期限切れのカートIDを使うとエラーになるので、エラー時は新規作成にフォールバック
エラーハンドリング
APIが失敗するケースを考慮しましょう。
よくあるエラー
| エラー | 原因 | 対処 |
|---|---|---|
| 在庫切れ | 商品が売り切れた | ユーザーに通知、入荷通知を提案 |
| カート期限切れ | 保存したカートIDが無効 | 新しいカートを作成 |
| ネットワークエラー | 通信が途切れた | リトライ、オフライン対応 |
構成図
最終的なシステム構成は以下のようになります。
カートUI(ボタン・ミニカート・スライド)+状態管理(Context/Zustand)+ローカルストレージ(カートID保存)
cartCreate / cartLinesAdd / cartLinesUpdate / cartLinesRemove
checkoutUrlで遷移・決済処理
まとめ
Shopify Storefront APIを使ったAjaxカートは、以下のポイントを押さえることで実装できます:
- cartCreate でカートを作成し、IDを保存
- cartLinesAdd/Update/Remove でカート操作
- React Context/Zustand で状態をグローバル管理
- 楽観的UI更新 で体感速度を向上
- エラーハンドリング で堅牢性を確保
これらを組み合わせることで、ページ遷移なしでスムーズな買い物体験を提供できます。