1. Top
  2. ブログ一覧
  3. Shopifyテーマカスタマイズで便利な Ajax APIの紹介 - カート編
Eコマース

Shopifyテーマカスタマイズで便利な Ajax APIの紹介 - カート編

公開日

2025.01.23

更新日

2025.01.23

Shopifyテーマカスタマイズで便利な Ajax APIの紹介 - カート編のサムネイル

カート API は、顧客のセッション中にカートを操作するために使用されます。このガイドでは、カート API を使用してカートの明細項目を更新し、カートの属性とメモを追加し、配送料を生成する方法を説明します。

Ajax APIのカート機能とは

Ajax APIのカート機能は、Shopifyのテーマ内で利用できる軽量のRESTエンドポイントを通じて、以下のような操作を非同期的に行う仕組みです。

  • 商品の追加・削除・数量変更
  • カート内商品の情報取得(タイトル、バリアント、価格など)
  • 配送料計算(配送オプションが表示される段階で活用)

これらは/{locale}/cart/{action}.jsの形式でエンドポイントにアクセスし、JSON形式でデータを送受信します。localeはストアの言語設定に合わせたレスポンスを受け取るためのパラメータです。

カートへ商品を追加する

/{locale}/cart/add.jsエンドポイントを使用して、1つまたは複数のバリエーションをカートに追加します。

次の例では、quantityは追加するバリエーションの数で、idはそのバリエーションのバリエーションIDとして配列にオブジェクトを追加することで、複数のバリエーションをカートに追加できます。
商品の種類のID・数量を指定して、カートに追加することができます。以下は、Ajax APIを利用してカートに商品を追加するサンプルコードです。

let formData = {
 'items': [{
  'id': 36110175633573,
  'quantity': 2
  }]
};

fetch(window.Shopify.routes.root + 'cart/add.js', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(formData)
})
.then(response => {
  return response.json();
})
.catch((error) => {
  console.error('Error:', error);
});

レスポンスは以下のようになります。

{
  "items": [
    {
      "id": 36110175633573,
      "title": "Red Rain Coat - Small",
      "key": "794864229:03af7a8cb59a4c3c45595c76fa8cb53c",
      "price": 12900,
      "line_price": 12900,
      "quantity": 2,
      "sku": null,
      "grams": 0,
      "vendor": "Shopify",
      "properties": {},
      "variant_id": 794864229,
      "gift_card": false,
      "url": "/products/red-rain-coat?variant=794864229",
      "featured_image": {
        "url": "http://cdn.shopify.com/s/files/1/0040/7092/products/red-rain-coat.jpeg?v=1402604893",
        "aspect_ratio": 1.0,
        "alt": "Red rain coat with a hood"
      },
      "image": "http://cdn.shopify.com/s/files/1/0040/7092/products/red-rain-coat.jpeg?v=1402604893",
      "handle": "red-rain-coat",
      "requires_shipping": true,
      "product_title": "Red Rain Coat",
      "product_description": "A bright red rain coat for rainy days!",
      "product_type": "Coat",
      "properties" : null,
      "variant_title": "Red",
      "variant_options": ["Red"],
      "options_with_values": [
        {
          "name": "Color",
          "value": "Red"
        }
      ]
    }
  ]
}

FormDataを使ってフォームからリクエストデータを作成して追加することもできます。

let addToCartForm = document.querySelector('form[action$="/cart/add"]');
let formData = new FormData(addToCartForm);

fetch(window.Shopify.routes.root + 'cart/add.js', {
  method: 'POST',
  body: formData
})
.then(response => {
  return response.json();
})
.catch((error) => {
  console.error('Error:', error);
});

アイテムにプロパティを追加する

リクエストデータにpropertiesを追加することで、カート内のアイテムにプロパティを追加することができます。

const requestData = {
  items: [
    {
      id: 36110175633573,
      quantity: 2,
      properties: {
        'First Name': 'John',
        'Last Name': 'Doe'
      }
    }
  ]
};

販売プランを追加する

リクエストデータにselling_planを追加することで、カート内のアイテムに販売プランを追加することができます。定期購入やサブスクリプションなど利用できる商品で有効です。

const requestData = {
    items: [
    {
      quantity: 1,
      id: 794864229,
      selling_plan: 183638
    }
  ]
};

カート情報を取得する

/{locale}/cart.jsエンドポイントを使用して、カート内のアイテムのリストを取得します。

以下は、Ajax APIを利用してカート内の商品情報を取得したサンプルデータです。カートのアイテム情報や合計金額、送料などが取得できます。

{
  "token": "Z2NwLXVzLXdlc3QxOjAxSjBQTVk1Sjc5NVFKTjNOVlhLWENXQUI1?key=0d9909213054e22d092152de385763f0",
  "note": "Hello!",
  "attributes": {
    "Gift wrap": "Yes"
  },
  "original_total_price": 3399,
  "total_price": 2925,
  "total_discount": 474,
  "total_weight": 500,
  "item_count": 2,
  "items": [
    {
      "id": 39897499729985,
      "properties": {},
      "quantity": 1,
      "variant_id": 39897499729985,
      "key": "39897499729985:b1fca88d0e8bf5290f306f808785f744",
      "title": "Health potion - S / Low",
      "price": 900,
      "original_price": 900,
      "discounted_price": 900,
      "line_price": 900,
      "original_line_price": 900,
      "total_discount": 0,
      "discounts": [],
      "sku": "",
      "grams": 500,
      "vendor": "Polina's Potent Potions",
      "taxable": true,
      "product_id": 6786188247105,
      "product_has_only_default_variant": false,
      "gift_card": false,
      "final_price": 900,
      "final_line_price": 900,
      "url": "/products/health-potion?selling_plan=610435137&variant=39897499729985",
      "featured_image": {
        "aspect_ratio": 1.5,
        "alt": "Health potion",
        "height": 1233,
        "url": "https://cdn.shopify.com/s/files/1/0561/7470/6753/files/science-beakers-blue-light-new.jpg?v=1683744744",
        "width": 1850
      },
      "image": "https://cdn.shopify.com/s/files/1/0561/7470/6753/files/science-beakers-blue-light-new.jpg?v=1683744744",
      "handle": "health-potion",
      "requires_shipping": true,
      "product_type": "Health",
      "product_title": "Health potion",
      "product_description": "Are you low on health? Well we've got the potion just for you!\nJust need a top up? Almost dead? In between? No need to worry because we have a range of sizes and strengths!",
      "variant_title": "S / Low",
      "variant_options": [
        "S",
        "Low"
      ],
      "options_with_values": [
        {
          "name": "Size",
          "value": "S"
        },
        {
          "name": "Strength",
          "value": "Low"
        }
      ],
      "line_level_discount_allocations": [],
      "line_level_total_discount": 0,
      "quantity_rule": {
        "min": 1,
        "max": null,
        "increment": 1
      },
      "has_components": false,
      "selling_plan_allocation": {
        "price_adjustments": [
          {
            "position": 1,
            "price": 900
          }
        ],
        "price": 900,
        "compare_at_price": 1000,
        "per_delivery_price": 900,
        "selling_plan": {
          "id": 610435137,
          "name": "Delivery every 1 Week",
          "description": null,
          "options": [
            {
              "name": "1 Week(s), 4 Week(s)",
              "position": 1,
              "value": "1 Week(s)"
            }
          ],
          "recurring_deliveries": true,
          "fixed_selling_plan": false,
          "price_adjustments": [
            {
              "order_count": null,
              "position": 1,
              "value_type": "percentage",
              "value": 10
            }
          ]
        }
      }
    },
    {
      "id": 39888235757633,
      "properties": {},
      "quantity": 1,
      "variant_id": 39888235757633,
      "key": "39888235757633:264890389795b468fe7b11339ff91c08",
      "title": "Whole bloodroot",
      "price": 2499,
      "original_price": 2499,
      "discounted_price": 2025,
      "line_price": 2025,
      "original_line_price": 2499,
      "total_discount": 474,
      "discounts": [
        {
          "amount": 250,
          "title": "Bloodroot discount!"
        },
        {
          "amount": 224,
          "title": "Ingredient Sale"
        }
      ],
      "sku": "",
      "grams": 0,
      "vendor": "Clover's Apothecary",
      "taxable": true,
      "product_id": 6792596455489,
      "product_has_only_default_variant": true,
      "gift_card": false,
      "final_price": 2249,
      "final_line_price": 2249,
      "url": "/products/bloodroot-whole?variant=39888235757633",
      "featured_image": {
        "aspect_ratio": 1.5,
        "alt": "Whole bloodroot",
        "height": 2973,
        "url": "https://cdn.shopify.com/s/files/1/0561/7470/6753/products/sweet-potato-pile.jpg?v=1650644893",
        "width": 4460
      },
      "image": "https://cdn.shopify.com/s/files/1/0561/7470/6753/products/sweet-potato-pile.jpg?v=1650644893",
      "handle": "bloodroot-whole",
      "requires_shipping": true,
      "product_type": "Fruits & Vegetables",
      "product_title": "Whole bloodroot",
      "product_description": "",
      "variant_title": null,
      "variant_options": [
        "Default Title"
      ],
      "options_with_values": [
        {
          "name": "Title",
          "value": "Default Title"
        }
      ],
      "line_level_discount_allocations": [
        {
          "amount": 250,
          "discount_application": {
            "type": "script",
            "key": "10a5c368-efe2-4d60-9082-9e6c298759e8",
            "title": "Bloodroot discount!",
            "description": null,
            "value": "2.5",
            "created_at": "2023-11-07T18:21:24.074Z",
            "value_type": "fixed_amount",
            "allocation_method": "across",
            "target_selection": "explicit",
            "target_type": "line_item",
            "total_allocated_amount": 250
          }
        }
      ],
      "line_level_total_discount": 250,
      "quantity_rule": {
        "min": 1,
        "max": null,
        "increment": 1
      },
      "has_components": false,
      "unit_price": 4050,
      "unit_price_measurement": {
        "measured_type": "weight",
        "quantity_value": "500.0",
        "quantity_unit": "g",
        "reference_value": 1,
        "reference_unit": "kg"
      }
    }
  ],
  "requires_shipping": true,
  "currency": "CAD",
  "items_subtotal_price": 3149,
  "cart_level_discount_applications": [
    {
      "type": "automatic",
      "key": "468373bf-8996-41ee-b735-790f915179f9",
      "title": "Ingredient Sale",
      "description": "",
      "value": "10.0",
      "created_at": "2023-11-07T18:20:58.649Z",
      "value_type": "percentage",
      "allocation_method": "across",
      "target_selection": "all",
      "target_type": "line_item",
      "total_allocated_amount": 224
    }
  ]
}

カート内のアイテムを更新する

/{locale}/cart/update.jsエンドポイントを使用して、カートの明細項目の数量、メモ、または属性を更新します。

アイテム数量を更新する

アイテムの更新情報を使い、update.jsエンドポイントを利用してカート内のアイテムの数量を更新することができます。
updatesオブジェクトには、値が希望の数量で、キーが次のいずれかであるキーと値のペアが含まれている必要があります。

let updates = {
  794864053: 2,
  794864233: 3
};

fetch(window.Shopify.routes.root + 'cart/update.js', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ updates })
})
.then(response => {
  return response.json();
})
.catch((error) => {
  console.error('Error:', error);
});

数量を 0 に設定すると、カートからアイテムを削除できます。

let updates = {
  794864053: 0,
  794864233: 0
};

fetch(window.Shopify.routes.root + 'cart/update.js', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ updates })
})
.then(response => {
  return response.json();
})
.catch((error) => {
  console.error('Error:', error);
});

カートのメモを更新する

カートのメモを更新するには、noteを含むオブジェクトを送信します。

const requestData = {
  note: 'This is a note about my order'
};

カートの属性を更新する

カートの属性を更新するには、attributesを含むオブジェクトを送信します。attributesオブジェクトには、更新する属性の名前、属性値であるキーと値のペアが含まれている必要があります。

const requestData = {
  attributes: {
    'Gift wrap': 'Yes',
    'Gift message': 'Happy Birthday!'
  }
};

カート内の1アイテムを更新する

/{locale}/cart/change.jsエンドポイントを使用して、カート内のアイテムの数量、メモ、または属性を更新します。

以下はアイテムの数量を更新するサンプルデータです。

const updateData = {
  id: 794864053,
  quantity: 2
};

配送料を計算する

  • /{locale}/cart/prepare_shipping_rates.jsonエンドポイントを使用して、カート内のアイテムの配送料を計算します。
  • /{locale}/cart/async_shipping_rates.jsonエンドポイントを使用して、カート内のアイテムの配送料を計算結果を取得します。

prepare_shipping_ratesのリクエスト例

/{locale}/cart/prepare_shipping_rates.json?shipping_address%5Bzip%5D=K1N+5T2&shipping_address%5Bcountry%5D=Canada&shipping_address%5Bprovince%5D=Ontario

async_shipping_ratesのリクエスト例

/{locale}/cart/async_shipping_rates.json?shipping_address%5Bzip%5D=K1N+5T2&shipping_address%5Bcountry%5D=Canada&shipping_address%5Bprovince%5D=Ontario

以下はレスポンスデータのサンプルです。

{
  "shipping_rates": [
    {
      "name": "Generic Rate",
      "presentment_name": "Generic Rate",
      "code": "Generic Rate",
      "price": "6.00",
      "markup": null,
      "source": "shopify",
      "delivery_date": null,
      "delivery_range": null,
      "delivery_days": [],
      "compare_price": null,
      "phone_required": false,
      "currency": null,
      "carrier_identifier": null,
      "delivery_category": null,
      "using_merchant_account": null,
      "carrier_service_id": null,
      "description": null,
      "api_client_id": null,
      "requested_fulfillment_service_id": null,
      "shipment_options": null,
      "charge_items": null,
      "has_restrictions": null,
      "rating_classification": null,
      "accepts_instructions": false
    },
    {
      "name": "Carrier Service Mail",
      "presentment_name": "Carrier Service Mail",
      "code": "CarrierServiceMail",
      "price": "12.46",
      "markup": "0.00",
      "source": "usps",
      "delivery_date": "2025-01-09",
      "delivery_range": [
          "2025-01-06",
          "2025-01-09"
      ],
      "delivery_days": [
          0,
          3
      ],
      "compare_price": null,
      "phone_required": true,
      "currency": null,
      "carrier_identifier": null,
      "delivery_category": null,
      "using_merchant_account": null,
      "carrier_service_id": 2,
      "description": null,
      "api_client_id": null,
      "requested_fulfillment_service_id": null,
      "shipment_options": null,
      "charge_items": null,
      "has_restrictions": null,
      "rating_classification": null,
      "accepts_instructions": false
      }
   ]
}

カートの内容をクリアする

/{locale}/cart/clear.jsエンドポイントを使用して、カート内のすべてのアイテムを削除することができます。

推定配送料を取得する

クイックに配送料を知るニーズに応えるために、/{locale}/cart/shipping_rates.jsonエンドポイントを使用して、カート内のアイテムの推定配送料を取得します。

/{locale}/cart/shipping_rates.jsonエンドポイントを使用して、カート内のアイテムの配送料を取得します。

/{locale}/cart/shipping_rates.json?shipping_address%5Bzip%5D=K1N+5T2&shipping_address%5Bcountry%5D=Canada&shipping_address%5Bprovince%5D=Ontario

バンドルセクションレンダリング

バンドルされたセクション レンダリングを使用すると、Cart API への最初の呼び出しに基づいて、同じ呼び出し内で更新する可能性のある最大 5 つのセクションの HTML マークアップを要求できます。

バンドル セクション レンダリングは、次の Cart API エンドポイントで利用できます。

  • /{locale}/cart/add
  • /{locale}/cart/change
  • /{locale}/cart/clear
  • /{locale}/cart/update

どのセクションにレンダリングさせるかは、sections パラメータで指定します。

const data = {
    items: [
    {
    id: 36110175633573,
    quantity: 2
    }
  ],
  sections: "cart-items,cart-icon-bubble,cart-live-region-text,cart-footer"
};

この場合は、例えばcart-itemsセクションのcartオブジェクトにレスポンスデータが格納されてレンダリングされる仕組みとなります。

既存テーマとAjax APIの関係

実は「カートに追加」ボタンや、カート画面における数量変更操作でAjax APIを通じてカート操作が行われています。

例えば「カートに追加」ボタンには以下のようなliquidコードが埋め込まれています。

{% form 'product', product %}
  <!-- form content -->
  <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
  <input type="hidden" name="quantity" value="1">
  <input type="submit" value="カートに追加">
{% endform %}

これは以下のように変換されます。

<form method="post" action="/cart/add" id="product_form_6786188247105" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data">
  <input type="hidden" name="form_type" value="product" />
  <input type="hidden" name="utf8" value="✓" />
  <!-- form content -->
  <input type="hidden" name="product-id" value="6786188247105" />
  <input type="hidden" name="variant-id" value="794864229" />
  <input type="hidden" name="quantity" value="1">
  <input type="submit" value="カートに追加">
</form>

このフォームが送信されると、Ajax APIの/{locale}/cart/add.jsエンドポイントにリクエストが送信され、カートに商品が追加されます。

基本的にAjax APIを使ってコーディングする機会はありませんが、カスタムでカート機能を拡張する際にはAjax APIを利用してボタンやフォームを実装することがあります。

まとめ

Shopify Ajax APIのカート機能を利用することで、ユーザーがページ遷移を意識せずに買い物を進められる、快適でスピーディな購買体験を提供できます。カートへの追加や削除、数量調整などをリアルタイムで反映させる仕組みは、売上やコンバージョン向上にも直結する大きな要素です。ストア運営の拡張性も高まり柔軟に対応できるメリットがあります。

Ajax APIを活用する際は、返ってくるJSONデータの構造とロケール対応をあらかじめ理解し、エラー処理を含むユーザー目線の作り込みを行うことが重要です。必要に応じて既存のテーマを参考にしながら、自身のショップに合わせたカスタマイズを検討してみてはいかがでしょうか。

参考