Shopify Cart APIの実装方法

Storefront APIのミューテーションと状態管理の実践的な解説

Shopify Storefront APICart APIGraphQLReact実装
読了時間: 13分

Shopify Storefront APIでカートを操作する

Ajaxカートを実装するには、Shopify Storefront APIの「Cart」機能を使います。このページでは、実際にどのようにAPIを呼び出し、フロントエンドの状態と連携させるかを解説します。

使用する主なAPI(ミューテーション)

Shopify Storefront APIはGraphQLベースです。カート操作には以下の「ミューテーション」を使います。

cartCreate
用途新しいカートを作成
cartLinesAdd
用途カートに商品を追加
cartLinesUpdate
用途商品の数量を変更
cartLinesRemove
用途カートから商品を削除
cartQuery
用途カートの中身を取得

全体の流れ

カート操作の基本的な流れは以下の通りです。

初回訪問時
ユーザーがサイトに来る
アクセス
cartCreateでカート作成
新規カート
カートIDをローカルストレージに保存
永続化
React Stateにカート情報をセット
状態管理
商品追加時
「カートに入れる」をクリック
ユーザー操作
React Stateを楽観的に更新
即座にUI反映
cartLinesAddミューテーション実行
API呼び出し
結果処理
API応答確認
成功
Stateを確定
失敗
Stateを元に戻し、エラー表示
再訪問時
ユーザーがサイトに来る
アクセス
ローカルストレージからカートID取得
復元
cartQueryでカートの中身を復元
データ取得
React Stateにセット
表示

カートの作成(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が無効
対処新しいカートを作成
ネットワークエラー
原因通信が途切れた
対処リトライ、オフライン対応

構成図

最終的なシステム構成は以下のようになります。

Ajaxカートシステム構成
フロントエンド (Next.js)

カートUI(ボタン・ミニカート・スライド)+状態管理(Context/Zustand)+ローカルストレージ(カートID保存)

GraphQL
Shopify Storefront API

cartCreate / cartLinesAdd / cartLinesUpdate / cartLinesRemove

チェックアウト
Shopify Checkout

checkoutUrlで遷移・決済処理

まとめ

Shopify Storefront APIを使ったAjaxカートは、以下のポイントを押さえることで実装できます:

  1. cartCreate でカートを作成し、IDを保存
  2. cartLinesAdd/Update/Remove でカート操作
  3. React Context/Zustand で状態をグローバル管理
  4. 楽観的UI更新 で体感速度を向上
  5. エラーハンドリング で堅牢性を確保

これらを組み合わせることで、ページ遷移なしでスムーズな買い物体験を提供できます。

関連記事