🍀

【Shopify.dev和訳】API usage/Bulk operations

2021/09/09に公開

この記事について

この記事は、API usage/Bulkoperationsの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://apps.shopify.com/shopify-application-314?locale=ja&from=daniel

Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。

https://apps.shopify.com/font-picker-1?locale=ja&from=daniel

GraphQL   AdminAPI を使用して一括操作を実行する

GraphQL Admin API を使用すると、一括操作を使用してデータを一括で非同期にフェッチできます。API は、大量のデータのページ付けを処理する際の複雑さを軽減するように設計されています。GraphQL AdminAPI スキーマで定義されている任意の接続フィールドを一括クエリできます。

結果を手動でページ付けしてクライアント側のスロットルを管理する代わりに、一括クエリ操作を実行できます。Shopify のインフラストラクチャは、クエリを実行するという大変な作業を行い、すべてのデータをダウンロードできる URL を提供します。

GraphQL Admin API は、単一の最上位フィールドのクエリを実行してから、返すフィールドを選択することをサポートしています。製品のバリエーションなど、接続をネストすることもできます。

アプリは、ショップごとに一度に 1 つの一括操作を実行するように制限されています。操作が完了すると、Shopify が URL で利用できるようにするJSONL file の形式で結果が配信されます。

一括クエリの導入

一括クエリを実行するための完全なフローについては後で説明しますが、以下は、すばやく開始するために使用できるいくつかの小さなコードスニペットです。

Step 1. クエリを送信する

bulkOperationRunQuery ミューテーションを実行し、Shopify に必要な情報を指定します。

次のミューテーションは、products の接続を照会し、各製品の ID とタイトルを返します。

POST /admin/api/2020-04/graphql.json

mutation {
  bulkOperationRunQuery(
    query: """
    {
      products {
        edges {
          node {
            id
            title
          }
        }
      }
    }
    """
  ) {
    bulkOperation {
      id
      status
    }
    userErrors {
      field
      message
    }
  }
}

JSON response

{
  "data": {
    "bulkOperationRunQuery": {
      "bulkOperation": {
        "id": "gid:\/\/shopify\/BulkOperation\/720918",
        "status": "CREATED"
      },
      "userErrors": []
    }
  },
...
}

Step 2. オペレーションのステータスをポーリングする

操作の実行中に、currentBulkOperationフィールドを使用して進行状況を確認するためにポーリングする必要があります。objectCountフィールドは増分して操作の進行状況を示し、status フィールドは操作が完了したかどうかを返します。

POST /admin/api/2020-04/graphql.json

query {
  currentBulkOperation {
    id
    status
    errorCode
    createdAt
    completedAt
    objectCount
    fileSize
    url
    partialDataUrl
  }
}

JSON response

{
  "data": {
    "currentBulkOperation": {
      "id": "gid:\/\/shopify\/BulkOperation\/720918",
      "status": "COMPLETED",
      "errorCode": null,
      "createdAt": "2019-08-29T17:16:35Z",
      "completedAt": "2019-08-29T17:23:25Z",
      "objectCount": "57",
      "fileSize": "358",
      "url": "https:\/\/storage.googleapis.com\/shopify\/dyfkl3g72empyyoenvmtidlm9o4g?<params>",
      "partialDataUrl": null
    }
  },
  ...
}

Step 3. データを取得する

操作が完了すると、URLフィールドで指定された URL から JSONL 出力ファイルをダウンロードできます。クエリで結果が生成されなかった場合、urlフィールドはnullを返します。

返されるファイルの詳細については結果データのダウンロード を、解析方法については JSONL ファイル形式JSONL file format を参照してください。


一括クエリワークフロー

以下は、一括クエリを作成するための高レベルのワークフローです。

  1. 潜在的な一括操作を特定する.
    新規または既存のクエリを使用できますが、大量のデータが返される可能性があります。接続ベースのクエリが最適です。

  2. Shopify GraphiQL アプリ.を使用してクエリをテストします。

  3. bulkOperationRunQueryのための新しいミューテーションドキュメントを作成します

  4. ミューテーションのqueryの値としてクエリを含めます。

  5. ミューテーションを実行します。

  6. statusフィールドに操作が実行されなくなったことが示されるまで、一括操作をポーリングしますobjectCountフィールドを使用して、操作の進行状況を確認できます 。

  7. URLフィールドに指定された URL で JSONL ファイルをダウンロードします

潜在的な一括クエリを特定する

大量のデータを返す可能性があり、一括操作のメリットを享受できる新規または既存のクエリを特定します。
結果のすべてのページを取得するためにページ付けを使用するクエリは、最も一般的な候補です。

以下のクエリ例は、2019 年 1 月 1 日以降に作成されたストアの最初の 50 個の商品からいくつかの基本情報を取得します。

{
  products(query: "created_at:>=2020-01-01 AND created_at:<2020-05-01", first: 50) {
    edges {
      cursor
      node {
        id
        createdAt
        updatedAt
        title
        handle
        descriptionHtml
        productType
        options {
          name
          position
          values
        }
        priceRange {
          minVariantPrice {
            amount
            currencyCode
          }
          maxVariantPrice {
            amount
            currencyCode
          }
        }
      }
    }
    pageInfo {
      hasNextPage
    }
  }
}

一括操作を書く

上記のクエリを一括クエリに変換するには、bulkOperationRunQueryミューテーションを使用します。queryのバリューなしでスケルトンミューテーションから始めるのが最も簡単です。

mutation {
  bulkOperationRunQuery(
    query: """

    """
  ) {
    bulkOperation {
      id
      status
    }
    userErrors {
      field
      message
    }
  }
}
  • 三重引用符( "" ")は、GraphQL で複数行の文字列を定義します。
  • 一括操作の ID が返されるため、操作をポーリングできます。
  • エラーメッセージを取得するために、userErrorsフィールドが返されます。

元のサンプルクエリをミューテーションに貼り付けてから、オプションでいくつかの小さな変更を加えます。

  • firstの引数はオプションであり、存在する場合は無視されるため、削除できます。
  • cursorフィールドとpageInfoフィールドもオプションであり、存在する場合は無視されるため、削除できます。
mutation {
  bulkOperationRunQuery(
    query: """
    {
      products(query: "created_at:>=2020-01-01 AND created_at:<2020-05-01") {
        edges {
          node {
            id
            createdAt
            updatedAt
            title
            handle
            descriptionHtml
            productType
            options {
              name
              position
              values
            }
            priceRange {
              minVariantPrice {
                amount
                currencyCode
              }
              maxVariantPrice {
                amount
                currencyCode
              }
            }
          }
        }
      }
    }
    """
  ) {
    bulkOperation {
      id
      status
    }
    userErrors {
      field
      message
    }
  }
}

ミューテーションが成功した場合、応答は次の例のようになります。

{
  "data": {
    "bulkOperationRunQuery": {
      "bulkOperation": {
        "id": "gid:\/\/shopify\/BulkOperation\/1",
        "status": "CREATED"
      },
      "userErrors": []
    }
  },
  ...
}

実行中の一括操作をポーリングする

一括操作は非同期で長時間実行されるため、いつ完了するかを調べるためにポーリングする必要があります。これを行う最も簡単な方法は、currentBulkOperationフィールドを照会することです。

{
  currentBulkOperation {
    id
    status
    errorCode
    createdAt
    completedAt
    objectCount
    fileSize
    url
    partialDataUrl
  }
}

このフィールドは、認証されたアプリとショップに対して(ステータスに関係なく)作成された最新の一括操作を返します。 ID で特定の操作を検索する場合は、nodeフィールドを使用できます。

{
  node(id: "gid://shopify/BulkOperation/1") {
    ... on BulkOperation {
      id
      status
      errorCode
      createdAt
      completedAt
      objectCount
      fileSize
      url
      partialDataUrl
    }
  }
}

予想されるデータ量に基づいて、ポーリング間隔を調整できます。たとえば、現在手動 ​​ でページ付けクエリを実行していて、すべての商品データを取得するのに 1 時間かかる場合、それは一括操作時間の概算として役立ちます。この状況では、1 分のポーリング間隔が 10 秒ごとよりもおそらく良いでしょう。

その他の可能な操作ステータスについては、BulkOperationStatus リファレンスを参照してください。

操作の進行状況を確認する

ポーリングは、操作が完了したかどうかを確認するのに役立ちますが、objectCountフィールドを使用して操作の進行状況を確認するためにも使用できます。

{
  currentBulkOperation {
    status
    objectCount
    url
  }
}

たとえば、1 か月で作成されたすべての商品をクエリしようとしていて、オブジェクト数が予想数を超えている場合は、クエリ条件が間違っていることを示している可能性があります。その場合、現在の操作をキャンセルして、別のクエリで新しい操作を実行することをお勧めします。


結果データをダウンロードする

操作の実行が終了すると、結果データが利用可能になります。

操作が正常に完了すると、urlフィールドにデータをダウンロードできる URL が含まれます。操作が失敗したが、失敗が発生する前に一部のデータが取得された場合、partialDataUrlフィールドで指定された URL で部分的に完全な出力ファイルを使用できます。いずれの場合も、返される URL は署名(認証)され、1 週間後に期限切れになります。

データをダウンロードしたので、JSONL 形式に従ってデータを解析します。


JSONL データ形式

通常の(バルクではない)GraphQL 応答は JSON です。応答構造はクエリ構造を反映しているため、ネストされたオブジェクトが多数含まれる単一の JSON オブジェクトになります。ほとんどの標準的な JSON パーサーでは、文字列またはファイル全体をメモリに読み込む必要があります。これにより、応答が大きい場合に問題が発生する可能性があります。

一括操作は大規模なデータセットをフェッチするように特別に設計されているため、クライアントがデータを消費する方法に柔軟性を持たせるために、応答データに JSON Lines (JSONL)形式を選択しました。JSONL は JSON に似ていますが、各行は独自の有効な JSON オブジェクトです。メモリ消費の問題を回避するために、ほとんどの言語にあるファイルストリーミング機能を使用して、ファイルを一度に 1 行ずつ解析できます。

ファイルの各行は、接続で返されるノードオブジェクトです。ノードにネストされた接続がある場合、各子ノードは次の行の独自のオブジェクトに抽出されます。たとえば、一括操作では、次のクエリを使用して、製品とそのネストされた variants 型のリストを取得できます。

{
  products {
    edges {
      node {
        id
        variants {
          edges {
            node {
              id
              title
            }
          }
        }
      }
    }
  }
}

JSONL の結果では、各製品オブジェクトの後に、新しい行にその variants 型オブジェクトが続きます。各接続タイプの順序は保持され、ネストされたすべての接続はファイル内の親の後に表示されます。ただし、子ノードは親の直後に表示されない場合があります。たとえば、次の結果では、タイトル52の製品 variants は、ファイルの最初の製品の子です。

{"id":"gid://shopify/Product/1921569226808"}
{"id":"gid://shopify/ProductVariant/19435458986040","title":"70","__parentId":"gid://shopify/Product/1921569226808"}
{"id":"gid://shopify/Product/1921569259576"}
{"id":"gid://shopify/ProductVariant/19435459018808","title":"34","__parentId":"gid://shopify/Product/1921569259576"}
{"id":"gid://shopify/Product/1921569292344"}
{"id":"gid://shopify/ProductVariant/19435459051576","title":"Default Title","__parentId":"gid://shopify/Product/1921569292344"}
{"id":"gid://shopify/Product/1921569325112"}
{"id":"gid://shopify/ProductVariant/19435459084344","title":"36","__parentId":"gid://shopify/Product/1921569325112"}
{"id":"gid://shopify/Product/1921569357880"}
{"id":"gid://shopify/ProductVariant/19435459117112","title":"47","__parentId":"gid://shopify/Product/1921569357880"}
{"id":"gid://shopify/ProductVariant/19435458986123","title":"52","__parentId":"gid://shopify/Product/1921569226808"}

ネストされた接続は応答データ構造にネストされなくなったため、結果にはオブジェクトの親への参照である__parentIdフィールドが含まれます。このフィールドは API スキーマに存在しないため、明示的にクエリすることはできません。一括操作結果に自動的に含まれます。

JSONL ファイルを逆に読む

JSONL ファイルを逆に読み取ると、子ノードのグループ化が容易になり、親ノードの後に ​​ 表示されるノードが失われる。たとえば、バリアントを収集している間、variants が属する製品に到達したときに、ファイルのさらに上にバリアントが存在することはありません。JSONL ファイルをダウンロードした後、それを逆に読み取り、それを解析して、親ノードが検出される前に子ノードが追跡されるようにします。

ほとんどのプログラミング言語には、ファイル全体がメモリに読み込まれないように、一度に 1 行ずつファイルを読み取る機能があります。この機能は、JSONL データファイルを処理するときに利用する必要があります。

これは、JSONL ファイルをロードして解析する適切な方法を示す Ruby の簡単な例です。

# Efficient: reads the file a single line at a time
File.open(file) do |f|
  f.each do |line|
    JSON.parse(line)
  end
end

# Inefficient: reads the entire file into memory
jsonl = File.read(file)

jsonl.each_line do |line|
  JSON.parse(line)
end

100MB の JSONL ファイルを使用した場合の違いを示すために、「良い」バージョンは 2.5MB のメモリしか消費しませんが、「悪い」バージョンは 100MB(ファイルサイズに等しい)を消費します。

他の言語:


実行の失敗

フィールドをクエリする権限がないなど、通常の GraphQL クエリが失敗する理由のいずれかにより、一括操作が失敗する可能性があります。このため、最初にクエリを通常どおり実行して、クエリが機能することを確認することをお勧めします。一括操作でクエリが失敗した場合よりも、はるかに優れたエラーフィードバックが得られます。

一括操作が失敗した場合、そのstatusフィールドはFAILEDを返し、errorCodeフィールドには次のコードが含まれます。

  • ACCESS_DENIED:アクセススコープがありません。クエリを通常どおり(一括操作以外で)実行して、問題の原因となっているフィールドの詳細を取得します。
  • INTERNAL_SERVER_ERROR:サーバーで問題が発生し、エラーが通知されました。これらのエラーは断続的に発生する可能性があるため、クエリの送信を再試行できます
  • TIMEOUT:実行中に 1 つ以上のクエリタイムアウトが発生しました。クエリを正常に実行できるように、クエリからいくつかのフィールドを削除してみてください。これらのタイムアウトは断続的に発生する可能性があるため、 クエリの送信を再試行できます

その他の考えられる操作エラーコードについては、BulkOperationErrorCode リファレンスを参照してください。

キャンセルされた操作

一括操作が停止した場合、Shopify によってキャンセルされる可能性があります。一括操作がキャンセルされた後、CANCELEDstatusが返されます。クエリを再送信することで、キャンセルされた一括操作を再試行できます。


操作のキャンセル

進行中の一括操作をキャンセルするには、操作 ID でbulkOperationCancelミューテーションを使用します。

mutation {
  bulkOperationCancel(id: "gid://shopify/BulkOperation/1") {
    bulkOperation {
      status
    }
    userErrors {
      field
      message
    }
  }
}

レート制限

現在、ショップごとに(アプリごとに)アクティブな(実行中の)一括操作は常に1 つだけです。この制限は、操作が非同期で長時間実行されるために適用されます。

一括操作が AdminAPI のレート制限内にどのように収まるか

バルク操作は、bulkOperationRunQueryミューテーション内にクエリ文字列を指定することにより、API コンシューマーであるユーザーによって開始されます。次に、Shopify はそのquery文字列を一括操作として非同期に実行します。 BulkOperationRunQueryミューテーションとバルクquery文字列自体のこの違いにより、レート制限の適用方法も決まります。ユーザーが行った GraphQL リクエストは、通常の API リクエストとしてカウントされ、レート制限の対象となりますが、一括操作クエリの実行は対象外です。

次の例では、(他のミューテーションと同様に)ミューテーションリクエストのコストが請求されますが、Shopify で一括操作として実行する製品タイトルのqueryについては請求されません。

mutation {
  bulkOperationRunQuery(
    query: """
    {
      products {
        edges {
          node {
            title
          }
        }
      }
    }
    """
  ) {
    bulkOperation {
      id
    }
  }
}

操作の作成、ステータスのポーリング、またはキャンセルのために低コストのリクエストを行うだけなので、一括操作は、標準のページ付けクエリと比較して、データをクエリするための非常に効率的な方法です。


動作制限

一括操作クエリには接続を含める必要があります。クエリが接続を使用しない場合は、通常の同期 GraphQL クエリとして実行する必要があります。

一括操作には、いくつかの追加の制限があります。

  • クエリ内の合計接続数は最大 5 つです。
  • 最上位のnodeおよび``nodes`フィールドは使用できません。
  • ネストされた接続の場合、最大 2 レベルの深さ。たとえば、ネストされた接続には 3 つのレベルがあるため、以下は無効です。
{
  products {
    edges {
      node {
        id
        variants {
          # nested level 1
          edges {
            node {
              id
              images {
                # nested level 2
                edges {
                  node {
                    id
                    metafields {
                      # nested level 3 (invalid)
                      edges {
                        node {
                          value
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

BulkOperationRunQueryミューテーションは、提供されたクエリを検証し、userErrorsフィールドを使用してエラーを提供します。
GraphQL クエリの柔軟性が許可されているものと提供されていないものの完全な例を提供するのは難しいので、いくつか試してみて、何が機能し、何が機能しないかを確認してください。まだサポートされていない有用なクエリを見つけた場合は、forumsでお知らせください。一般的な使用例を収集できます。


次のステップ

GraphQL AdminAPI を使用したデータの一括インポート

従来の同期 API を使用して大量のデータをインポートすると、処理が遅く、実行が複雑になり、管理が困難になります。GraphQL ミューテーションを手動で複数回実行し、クライアント側のスロットルを管理する代わりに、バルクミューテーション操作を実行できます。

GraphQL Admin API を使用すると、大量のデータを非同期で一括インポートできます。操作が完了すると、Shopify が URL で利用できるようにするJSON Lines (JSONL)ファイルで結果が配信されます。

このガイドでは、bulkOperationRunMutationを紹介し、それを使用してデータを Shopify に一括インポートする方法を示します。


要件


制限事項

この制限は、操作が非同期で長時間実行されるために適用されます。ショップに対して後続のバルクミューテーション操作を実行するには、実行中の操作をキャンセルするか、実行が終了するのを待つ必要があります。


データの一括インポートの仕組み

バルク操作を開始するには、bulkOperationRunMutationにミューテーション文字列を指定します。次に、Shopify は、そのミューテーション文字列を一括操作として非同期に実行します。

作成するほとんどの GraphQLAdmin API リクエストにはレート制限が適用されますが、bulkOperationRunMutationリクエストには適用されません。操作の作成、ステータスのポーリング、またはキャンセルのために低コストのリクエストを行うだけなので、バルクミューテーション操作は、標準の GraphQLAPI リクエストと比較してデータを作成するための効率的な方法です。

次の図は、Shopify へのデータの一括インポートに関連する手順を示しています。

Workflow for bulk importing data

  1. JSONL ファイルを作成し、GraphQL 変数を含めます :ミューテーションの変数を JSONL ファイル形式で含めます。 JSONL ファイルの各行は、1 つの入力ユニットを表します。ミューテーションは、入力ファイルの各行で 1 回実行されます。
  2. ファイルを Shopify にアップロードする:ファイルをアップロードする前に、stagedUploadsCreateミューテーションを実行してリンクを予約する必要があります。スペースが予約された後、stagedUploadsCreate応答から返された情報を使用して要求を行うことにより、ファイルをアップロードできます。
  3. 一括変更操作の作成:ファイルがアップロードされたら、bulkOperationRunMutationを実行して一括変更操作を作成できます。 bulkOperationRunMutationは、最後のステップでアップロードされた変数のファイルを使用して、提供された GraphQL API ミューテーションを実行することにより、データを一括でインポートします。
  4. 操作のステータスをポーリングする:操作の実行中に、currentBulkOperationフィールドを使用して進行状況を確認するためにポーリングする必要があります。bulkOperationオブジェクトのobjectCountフィールドは、操作の進行状況を示すために増分し、statusフィールドは、操作が完了したかどうかを示すブール値を返します。
  5. 結果の取得:一括変更操作が完了すると、URLフィールドで指定された URL から JSONL 出力ファイルをダウンロードできます。

JSONL ファイルを作成し、GraphQL 変数を含めます

GraphQL 変数を新しい JSONL ファイルに追加するときは、対応する一括操作 GraphQLAPI によって受け入れられるように変数をフォーマットする必要があります。入力変数の形式は、GraphQL AdminAPI スキーマと一致する必要があります。

たとえば、大量の製品をインポートしたい場合があります。製品の各属性は、GraphQL 入力オブジェクト ProductInputで定義された既存のフィールドにマップする必要があります。JSONL ファイルでは、各行は 1 つの製品入力を表します。 GraphQL Admin API は、入力ファイルの各行で 1 回実行されます。入力オブジェクトの構造がどれほど複雑であっても、1 つの入力は 1 行だけを占める必要があります。

次の例は、10 個の製品をまとめて作成するために使用されるサンプル JSONL ファイルを示しています。

{ "input": { "title": "Sweet new snowboard 1", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 2", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 3", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 4", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 5", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 6", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 7", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 8", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 9", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 10", "productType": "Snowboard", "vendor": "JadedPixel" } }

ファイルを Shopify にアップロードします

JSONL ファイルを作成し、GraphQL 変数を含めたら、ファイルを Shopify にアップロードできます。ファイルをアップロードする前に、まずアップロード URL とパラメーターを生成する必要があります。

アップロードされた URL とパラメータを生成します

stagedUploadsCreateミューテーションを使用して、アップロードの認証に必要な値を生成できます。ミューテーションは、stagedMediaUploadTargetインスタンスの配列を返します。

stagedMediaUploadTargetのインスタンスには、次の主要なプロパティがあります。

  • parameters:アップロードリクエストの認証に使用するパラメータ。
  • url:GraphQL 変数を含む JSONL ファイルをアップロードできる署名付き URL。

ミューテーションは、次のフィールドを持つタイプstagedUploadInputの入力を受け入れます。

フィールド 説明
resource enum アップロードするリソースタイプを指定します。 BulkOperationRunMutationを使用するには、リソースタイプがBULK_MUTATION_VARIABLESである必要があります。
filename string アップロードするファイルの名前。
mimeType string アップロードするファイルのmedia typeBulkOperationRunMutationを使用するには、mimeType「text / jsonl」である必要があります。
httpMethod enum 段階的アップロードで使用される HTTP メソッド。 BulkOperationRunMutationを使用するには、httpMethodPOSTである必要があります。

次の例では、stagedUploadsCreateミューテーションを使用して、JSONL ファイルをアップロードし、bulkOperationRunMutationによって消費されるために必要な値を生成します。最初に変数なしでstagedUploadsCreateミューテーションを実行してから、JSONL データを使用して段階的なアップロード URL に POST リクエストを個別に送信する必要があります。

リクエスト
POST /admin/api/2021-07/graphql.json

mutation {
  stagedUploadsCreate(
    input: {
      resource: BULK_MUTATION_VARIABLES
      filename: "bulk_op_vars"
      mimeType: "text/jsonl"
      httpMethod: POST
    }
  ) {
    userErrors {
      field
      message
    }
    stagedTargets {
      url
      resourceUrl
      parameters {
        name
        value
      }
    }
  }
}

JSON response:

{
  "data": {
    "stagedUploadsCreate": {
      "userErrors": [],
      "stagedTargets": [
        {
          "url": "https://shopify.s3.amazonaws.com",
          "resourceUrl": null,
          "parameters": [
            {
              "name": "key",
              "value": "tmp/21759409/bulk/89e620e1-0252-43b0-8f3b-3b7075ba4a23/bulk_op_vars"
            },
            {
              "name": "Content-Type",
              "value": "text/jsonl"
            },
            {
              "name": "success_action_status",
              "value": "201"
            },
            {
              "name": "acl",
              "value": "private"
            },
            {
              "name": "policy",
              "value": "eyJleHBpcmF0aW9uIjoiMjAyMS0wMS0yOFQyMDowNTo0NloiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJzaG9waWZ5In0sWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMSwyMDk3MTUyMF0seyJrZXkiOiJ0bXAvMjE3NTk0MDkvYnVsay84OWU2MjBlMS0wMjUyLTQzYjAtOGYzYi0zYjcwNzViYTRhMjMvYnVsa19vcF92YXJzIn0seyJDb250ZW50LVR5cGUiOiJ0ZXh0L2pzb25sIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSx7IngtYW16LWNyZWRlbnRpYWwiOiJBS0lBSllNNTU1S1ZZRVdHSkRLUS8yMDIxMDEyOC91cy1lYXN0LTEvczMvYXdzNF9yZXF1ZXN0In0seyJ4LWFtei1hbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In0seyJ4LWFtei1kYXRlIjoiMjAyMTAxMjhUMTkwNTQ2WiJ9XX0="
            },
            {
              "name": "x-amz-credential",
              "value": "AKIAJYM555KVYEWGJDKQ/20210128/us-east-1/s3/aws4_request"
            },
            {
              "name": "x-amz-algorithm",
              "value": "AWS4-HMAC-SHA256"
            },
            {
              "name": "x-amz-date",
              "value": "20210128T190546Z"
            },
            {
              "name": "x-amz-signature",
              "value": "5d063aac44a108f2e38b8294ca0e82858e6f44baf835eb81c17d37b9338b5153"
            }
          ]
        }
      ]
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 11,
      "actualQueryCost": 11
    }
  }
}

JSON ファイルをアップロードする

アップロード用のパラメーターと URL を生成した後、POST リクエストを使用して JSONL ファイルをアップロードできます。マルチパートフォームを使用し、すべてのパラメーターをフォーム入力としてリクエスト本文に含める必要があります。

マルチパートフォームのパラメーターを生成するには、stagedUploadsCreateミューテーションから返されたパラメーターから始めます。次に、添付ファイルを追加します。

POST request

curl --location --request POST 'https://shopify.s3.amazonaws.com' \
--form 'key="tmp/21759409/bulk/89e620e1-0252-43b0-8f3b-3b7075ba4a23/bulk_op_vars"' \
--form 'x-amz-credential="AKIAJYM555KVYEWGJDKQ/20210128/us-east-1/s3/aws4_request"' \
--form 'x-amz-algorithm="AWS4-HMAC-SHA256"' \
--form 'x-amz-date="20210128T190546Z"' \
--form 'x-amz-signature="5d063aac44a108f2e38b8294ca0e82858e6f44baf835eb81c17d37b9338b5153"' \
--form 'policy="eyJleHBpcmF0aW9uIjoiMjAyMS0wMS0yOFQyMDowNTo0NloiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJzaG9waWZ5In0sWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMSwyMDk3MTUyMF0seyJrZXkiOiJ0bXAvMjE3NTk0MDkvYnVsay84OWU2MjBlMS0wMjUyLTQzYjAtOGYzYi0zYjcwNzViYTRhMjMvYnVsa19vcF92YXJzIn0seyJDb250ZW50LVR5cGUiOiJ0ZXh0L2pzb25sIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSx7IngtYW16LWNyZWRlbnRpYWwiOiJBS0lBSllNNTU1S1ZZRVdHSkRLUS8yMDIxMDEyOC91cy1lYXN0LTEvczMvYXdzNF9yZXF1ZXN0In0seyJ4LWFtei1hbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In0seyJ4LWFtei1kYXRlIjoiMjAyMTAxMjhUMTkwNTQ2WiJ9XX0="' \
--form 'acl="private"' \
--form 'Content-Type="text/jsonl"' \
--form 'success_action_status="201"' \
--form 'file=@"/Users/username/Documents/bulk_mutation_tests/products_long.jsonl"'

GraphQL variables in JSONL file

{ "input": { "title": "Sweet new snowboard 1", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 2", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 3", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 4", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 5", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 6", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 7", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 8", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 9", "productType": "Snowboard", "vendor": "JadedPixel" } }
{ "input": { "title": "Sweet new snowboard 10", "productType": "Snowboard", "vendor": "JadedPixel" } }

バルクミューテーション操作を作成する

ファイルをアップロードした後、bulkOperationRunMutationを実行して、データを一括でインポートできます。対応するミューテーションと前の手順で取得した URL を指定する必要があります。

bulkOperationRunMutationミューテーションは、次の引数を取ります。

フィールド 説明
mutation string 一括で実行する GraphQLAPI ミューテーションを指定します。有効な値:productCreatecollectionCreateproductUpdateproductUpdateMediaproductVariantUpdate
stagedUploadPath string stagedUploadsCreateによって使用される JSONL 形式の入力ファイルへのパス

次の例では、次のproductCreateミューテーションを一括で実行します。

mutation call($input: ProductInput!) {
  productCreate(input: $input) {
    product {
      id
      title
      variants(first: 10) {
        edges {
          node {
            id
            title
            inventoryQuantity
          }
        }
      }
    }
    userErrors {
      message
      field
    }
  }
}

productCreateミューテーションを一括で実行するには、ミューテーションを文字列としてbulkOperationRunMutationに渡します。

Request
POST /admin/api/2021-07/graphql.json

mutation {
  bulkOperationRunMutation(
    mutation: "mutation call($input: ProductInput!) { productCreate(input: $input) { product {id title variants(first: 10) {edges {node {id title inventoryQuantity }}}} userErrors { message field } } }"
    stagedUploadPath: "tmp/21759409/bulk/89e620e1-0252-43b0-8f3b-3b7075ba4a23/bulk_op_vars"
  ) {
    bulkOperation {
      id
      url
      status
    }
    userErrors {
      message
      field
    }
  }
}

JSON response:

{
  "data": {
    "bulkOperationRunMutation": {
      "bulkOperation": {
        "id": "gid://shopify/BulkOperation/206005076024",
        "url": null,
        "status": "CREATED"
      },
      "userErrors": []
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 10,
      "actualQueryCost": 10
    }
  }
}

操作のステータスをポーリングします

一括操作は非同期であり、大きな入力ファイルの場合は長時間実行される可能性があるため、ポーリングを実行して、いつ完了したかを確認する必要があります。ポーリングする最も簡単な方法は、currentBulkOperationフィールドを照会することです。

インポートするデータの量に基づいて、ポーリング間隔を調整できます。その他の考えられる操作ステータスについては、BulkOperationStatusリファレンスドキュメントを参照してください。

操作のステータスをポーリングするには、次のリクエスト例を使用します。

Request
POST /admin/api/2021-07/graphql.json

query {
  currentBulkOperation(type: MUTATION) {
    id
    status
    errorCode
    createdAt
    completedAt
    objectCount
    fileSize
    url
    partialDataUrl
  }
}

JSONresponse

{
  "data": {
    "currentBulkOperation": {
      "id": "gid://shopify/BulkOperation/206005076024",
      "status": "COMPLETED",
      "errorCode": null,
      "createdAt": "2021-01-28T19:10:59Z",
      "completedAt": "2021-01-28T19:11:09Z",
      "objectCount": "16",
      "fileSize": "6155",
      "url": "https://storage.googleapis.com/shopify-tiers-assets-prod-us-east1/iqtpj52yuoa7prkbpzp9gwn27kw3?GoogleAccessId=assets-us-prod%40shopify-tiers.iam.gserviceaccount.com&Expires=1612465869&Signature=KOhlcYhLve3NLr6rfVbAeY02crFAM3rMrDNfTSlgT%2FScI%2B8o%2B%2FdO99F3UseC837uWA6FzfrNhxdRNqhBN%2F8ekBTW7IyPRD6ho5phfE8MTaev4ltQrJygJTDbjXfX5KLJOuY8siH%2FDrc4gctZsMsNaf2%2FYp%2FaDzBzjfxJge8i8he69t0uZ39FBXrMxCeMVd6lU8%2FbgMuO80rTHjgI%2BlC8g2%2FWiHyq5rSTDLIxxGWRCddMfPcaivdWVdMubMa0wOt9W9R2mfjuTAgUBexUkJwhvrkdof%2Bg00gU1g4dIBWlUSO5D9tdrv9bmIy7FceopNufrpwnD1NXU8Narsx2yEQ6aA%3D%3D&response-content-disposition=attachment%3B+filename%3D%22bulk-206005076024.jsonl%22%3B+filename%2A%3DUTF-8%27%27bulk-206005076024.jsonl&response-content-type=application%2Fjsonl",
      "partialDataUrl": null
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 1,
      "actualQueryCost": 1
    }
  }
}

結果を取得する

バルクミューテーション操作が終了したら、結果データファイルをダウンロードできます。

操作が正常に完了すると、urlフィールドにはデータファイルをダウンロードできる URL が含まれます。操作が失敗したが、失敗が発生する前に一部のデータが取得された場合、partialDataUrlフィールドで指定された URL で部分的に完全なデータファイルを利用できます。

いずれの場合も、返された URL は認証され、1 週間後に期限切れになります。

データをダウンロードしたら、JSONL 形式に従って解析できます。入力ファイルと応答ファイルの両方が JSONL にあるため、最終的なアセットファイルの各行は、入力ファイルの対応する行でミューテーションを実行したときの応答を表します。

操作の成功

次の例は、正常に作成された製品の応答を示しています。

{
  "data": {
    "productCreate": {
      "product": {
        "id": "gid://shopify/Product/5602345320504",
        "title": "Monday morning snowboard 1",
        "variants": {
          "edges": [
            {
              "node": {
                "id": "gid://shopify/ProductVariant/35645836853304",
                "title": "First",
                "inventoryQuantity": 0
              }
            },
            {
              "node": {
                "id": "gid://shopify/ProductVariant/35645836886072",
                "title": "Second",
                "inventoryQuantity": 0
              }
            }
          ]
        }
      },
      "userErrors": []
    }
  },
  "__lineNumber": 0
}

操作の失敗

特定の API にアクセスする権限がないなど、通常の GraphQL API ミューテーションが失敗する理由のいずれかにより、一括操作が失敗する可能性があります。このため、最良のアプローチは、最初に単一の GraphQL ミューテーションを実行して、バルク操作の一部としてミューテーションを実行する前に、それが機能することを確認することです。

一括操作が失敗した場合、そのstatusフィールドはFAILEDを返し、errorCodeフィールドは次のいずれかのコードを返します。

  • ACCESS_DENIED:アクセススコープがありません。ミューテーションを通常どおり(一括操作の外で)実行して、問題の原因となっているフィールドの詳細を取得します。
  • INTERNSL_SERVER_ERROR:Shopify のサーバーで問題が発生し、エラーが通知されました。これらのエラーは断続的に発生する可能性があるため、もう一度リクエストしてみてください。
  • TIMEOUT:実行中に 1 つ以上のミューテーションタイムアウトが発生しました。クエリを正常に実行できるように、クエリからいくつかのフィールドを削除してみてください。これらのタイムアウトは断続的に発生する可能性があるため、クエリの送信を再試行できます。

その他の考えられる操作エラーコードについては、BulkOperationErrorCodeリファレンスドキュメントを参照してください。

検証エラー

入力の形式が正しいが、1 つ以上の値が製品作成サービスの検証に失敗した場合、応答は次のようになります。

{"data"=>{"productCreate"=>{"userErrors"=>[{"message"=>"Some error message", "field"=>["some field"]}]}}}

認識できないフィールドエラー

入力に認識できないフィールドがある場合、応答は次のようになります。

{"errors"=>[{"message"=>"Variable input of type ProductInput! was provided invalid value for myfavoriteaddress (Field is not defined on ProductInput)", "locations"=>[{"line"=>1, "column"=>13}], "extensions"=>{"value"=>{"myfavoriteaddress"=>"test1"}, "problems"=>[{"path"=>["myfavoriteaddress"], "explanation"=>"Field is not defined on ProductInput"}]}}]}

操作をキャンセルする

進行中のバルク操作をキャンセルするには、 bulkOperationCancelミューテーションを実行し、操作 ID を入力変数として指定します。
Request
POST /admin/api/2021-07/graphql.json

mutation {
  bulkOperationCancel(id: "gid://shopify/BulkOperation/1") {
    bulkOperation {
      status
    }
    userErrors {
      field
      message
    }
  }
}

次のステップ

Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://apps.shopify.com/shopify-application-314?locale=ja&from=daniel

Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。

https://apps.shopify.com/font-picker-1?locale=ja&from=daniel

Discussion

ログインするとコメントできます