Shopify運用設計研究所 > Shopify GraphQL設計ガイド|クエリ・mutation・Bulk Operationsを実装前に整理する

Shopify GraphQL設計ガイド|クエリ・mutation・Bulk Operationsを実装前に整理する

2026年7月3日

18分で読めます

Shopify GraphQL Admin APIで商品、注文、メタフィールドを扱う前に、QueryRoot、query/mutation、variables、global ID、fields設計、connections、query cost、userErrors、再実行、Bulk Operationsへの切替基準を整理します。

Shopify
GraphQL
Shopify Admin API
EC開発
API設計
助手
助手

Shopify GraphQLで商品や注文を取るクエリは書けそうです。あとは実装しながら調整すればよいですか?

博士
博士

動くクエリを書くことと、本番で運用できるクエリを設計することは別です。どのfieldsを取るか、何件ずつページングするか、costをどう見るか、mutation失敗時にどこから再実行するかを先に決めないと、あとで復旧できない連携になります。

「shopify graphql」で調べる人は、GraphQLの文法だけを知りたいわけではありません。商品、注文、顧客、メタフィールドを必要な分だけ取りたい。RESTより柔軟に取れると聞いたが、どこまでネストしてよいか分からない。first: 250で回せばよいのか、Bulk Operationsにすべきなのか、mutationのuserErrorsをどう扱うべきか判断したい。そういう実装直前の悩みが多いはずです。

Shopify公式のGraphQL Admin API referenceでは、Admin APIのGraphQLスキーマ、QueryRoot、Products、Orders、Metafieldsなどの型が確認できます。2026年7月3日時点のlatestは2026-07です。公式Docsは正確ですが、実務では「このクエリは書ける」だけでは足りません。

この記事では、どのAPIを選ぶかを扱うShopify APIの種類と選び方や、Admin API全体の実装計画を扱うShopify Admin API実装計画とは重複させず、GraphQLそのもののquery/mutation設計に絞ります。主役は、過取得を避けるfields設計、cursor pagination、query cost、userErrors、再実行、Bulk Operationsへの切替です。

Shopify GraphQL設計で最初に決めるべきなのは、取得できる最大項目ではなく、業務判断に必要な最小fieldsと、失敗時に再実行できる処理単位です。

先に結論:GraphQL設計は6つに分ける

Shopify GraphQLは、必要なfieldsだけを指定して取得できるため、RESTより柔軟です。しかし柔軟な分、設計を誤ると「1回のクエリが重い」「ページング途中で止まる」「mutationが一部失敗したのに成功扱いになる」「同じ注文を二重連携する」という事故につながります。

実装前には、次の6つを分けて設計します。

設計項目 決めること 決めないと起きること
スキーマ探索 GraphiQLでQueryRoot、対象型、入力型、戻り値を確認する 古いサンプルを流用し、現行versionで使えないfieldに依存する
fields設計 商品、注文、メタフィールドごとに必要最小限のfieldsを決める 便利だから全部取るクエリになり、costと保守負荷が増える
pagination設計 firstafterpageInfo、cursor保存単位を決める 途中停止後に先頭からやり直し、重複処理や欠損が起きる
cost設計 requested/actual cost、throttleStatus、分割基準を見る 本番件数で急にrate limitに当たり、夜間バッチが終わらない
mutation設計 variables、global ID、userErrors、部分失敗、冪等性を決める HTTP 200を成功扱いし、失敗した商品や注文を見落とす
Bulk判断 通常Queryで回す範囲とBulk Operationsへ逃がす範囲を決める 全件同期を通常Queryで押し切り、遅くて壊れやすい処理になる

GraphQLの設計は、文法の話ではありません。EC担当が見たい商品、CSが追う注文、倉庫が使う在庫、外部DBが持つID、再実行時のログまで含めた業務処理の設計です。

GraphiQLでQueryRootから探索する

Shopify GraphQLの入口はQueryRootです。公式Docsでも、クエリはQueryRootにあるオブジェクトから始まると説明されています。また、GraphiQL ExplorerやShopifyのGraphiQL appを使うと、ストアデータに対してquery/mutationを試しながらスキーマを探索できます。

最初にやるべきことは、完成クエリを探すことではなく、対象業務に必要な型を確認することです。

探索するもの 見るポイント
QueryRoot どの入口から取るか productsordersproductordernodes
Object type 取得できるfields ProductProductVariantOrderMetafield
Connection 一覧取得時の構造 ProductConnectionOrderConnection
Edge/Node 一覧内の要素とcursor edges { cursor node { ... } }
PageInfo 次ページの有無 hasNextPageendCursor
Input type mutationに渡すvariables MetafieldsSetInputなど
Payload mutationの戻り値 更新後の対象、userErrors

GraphiQLで確認したクエリは、そのまま本番コードに貼るのではなく、operation名、variables、ログ項目、再実行キーを付けて実装用に整えます。

query ProductSyncProbe($first: Int!, $after: String) {
  products(first: $first, after: $after, query: "status:active") {
    nodes {
      id
      title
      handle
      updatedAt
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

variablesを使う理由は、クエリ文字列を毎回組み立てないためです。firstafter、検索条件、対象IDをvariablesに出しておけば、ログにも残しやすく、再実行もしやすくなります。

query/mutationを業務単位に分ける

GraphQLでは、読み取りをquery、書き込みをmutationで扱います。Shopifyでは、商品一覧を取る、注文詳細を取る、メタフィールドを書く、タグを付ける、といった処理をそれぞれ別のoperationとして設計します。

業務処理 queryで読むもの mutationで書くもの 分ける理由
商品同期 商品ID、handle、status、SKU、必要なメタフィールド 外部商品ID、同期済みフラグ、商品メタフィールド 販売文言と外部同期項目を混ぜない
注文連携 注文ID、name、createdAt、支払状態、明細、配送先、顧客 連携済みタグ、外部WMS ID、注文メタフィールド 出荷対象判定と連携結果の書き戻しを分ける
メタフィールド更新 ownerId、namespace、key、type、既存値 metafieldsSetなど 失敗項目だけ再実行できるようにする
差分確認 updatedAt、status、対象ID 原則書かない 読む処理と書く処理を分離し、障害時の影響を狭める
手動再実行 対象ID、前回エラー、最新状態 必要なmutationだけ 全バッチを再実行せず、対象単位で復旧する

Admin APIの全体設計では認証、スコープ、API version、Webhookも重要です。ただし、GraphQL query/mutation設計だけを見るなら、ポイントは「1つのoperationが何の業務結果を作るか」です。1本の巨大なqueryで商品、注文、顧客、メタフィールドを全部見るより、処理単位で分けた方がログも再実行も安定します。

global IDと外部キーの対応を保存する

Shopify GraphQLでは、gid://shopify/Product/123456789のようなglobal IDを使います。商品、バリエーション、注文、顧客、メタフィールド、ロケーションなど、mutationの対象指定でもglobal IDが必要になります。

実務では、global IDだけでも、SKUだけでも足りません。外部DBやWMS、PIM、CRMが持つIDとShopify側のIDを対応させる必要があります。

対象 Shopify GraphQLで使うID 外部で持ちがちなキー 保存方針
商品 Product global ID 外部商品ID、handle、商品コード 外部商品IDとProduct IDの対応を保存する
バリエーション ProductVariant global ID SKU、JAN、色サイズコード SKUとVariant IDの対応を保存する
注文 Order global ID 注文番号、WMS注文ID、会計伝票ID Order IDと外部連携ID、処理状態を保存する
顧客 Customer global ID メール、CRM顧客ID メール変更を前提に、Customer IDを保存する
メタフィールド ownerIdとnamespace/key 外部属性名、項目コード ownerId、namespace、key、typeを台帳化する

SKUやメールは業務上分かりやすいですが、変更される可能性があります。mutationの再実行では、対象を取り違えないことが最優先です。GraphQL IDと外部キーの対応表を持ち、再実行時には最新状態をqueryで確認してからmutationを投げます。

メタフィールドのnamespace/key/typeの考え方は、Shopifyメタフィールド設計ガイドでも詳しく整理しています。GraphQL実装では、それをさらにownerId単位の実行設計へ落とします。

過取得を避けるfields設計

GraphQLは、欲しいfieldsを選べます。だからこそ、設計しないと「今後使うかもしれない」項目が増え続けます。商品詳細、注文明細、顧客、メタフィールド、フルフィルメントまで深くネストすると、query costもレスポンスサイズも大きくなります。

fields設計では、画面や連携先が本当に使う項目だけを残します。

対象 最初に取るfields 後回しにするfields 理由
products idtitlehandlestatusupdatedAt 画像全件、説明HTML、全variants、全metafields 商品一覧同期では識別と差分判定を優先する
variants idskudisplayName、必要な価格/在庫参照 すべてのselectedOptions、画像、販売計画 SKU単位で使う項目だけに絞る
orders idnamecreatedAtdisplayFinancialStatusdisplayFulfillmentStatus 返金、イベント履歴、全顧客属性 出荷や連携判定に必要な状態を優先する
lineItems idquantityskuvariant { id } 商品説明、画像、全discountAllocations WMSや会計へ渡す粒度に合わせる
metafields namespacekeytypevalue 使わないnamespaceの全件 対象namespace/keyを明示して取得する

たとえば商品同期で必要なのが外部商品IDと配送温度帯だけなら、メタフィールド全件を取る必要はありません。注文連携で必要なのがSKU、数量、配送先、支払状態なら、商品説明HTMLや画像URLを同じqueryに入れない方がよいです。

GraphQLの「必要なfieldsだけ取れる」は、自由にたくさん取れるという意味ではなく、業務に不要なfieldsを取らない責任が実装側にあるという意味です。

products/orders/metafieldsのquery例を分ける

1つの万能queryを作るより、目的別に小さく分けます。以下は考え方の例です。実装時はストアのスコープ、API version、対象件数、必要fieldsに合わせて調整します。

商品差分を見るqueryでは、まず識別と更新日時を優先します。

query ProductsForDeltaSync($first: Int!, $after: String) {
  products(first: $first, after: $after, query: "status:active") {
    nodes {
      id
      title
      handle
      status
      updatedAt
      variants(first: 20) {
        nodes {
          id
          sku
          displayName
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

注文連携では、出荷や外部連携に必要なfieldsだけに絞ります。

query OrdersForFulfillmentExport($first: Int!, $after: String) {
  orders(first: $first, after: $after, query: "financial_status:paid fulfillment_status:unfulfilled") {
    nodes {
      id
      name
      createdAt
      displayFinancialStatus
      displayFulfillmentStatus
      lineItems(first: 50) {
        nodes {
          id
          quantity
          sku
          variant {
            id
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

メタフィールドは、ownerとnamespace/key/typeを意識します。

query ProductMetafieldsForIntegration($id: ID!) {
  product(id: $id) {
    id
    title
    metafields(first: 20, namespace: "integration") {
      nodes {
        id
        namespace
        key
        type
        value
      }
    }
  }
}

ここで大事なのは、例のfieldsをそのまま使うことではありません。業務処理ごとに「このfieldsを使って何を判断するか」を書ける状態にすることです。

connections/nodes/edges/pageInfoを設計する

Shopify公式のPaginating results with GraphQLでは、connections、nodes、edges、pageInfo、forward paginationが説明されています。forward paginationでは、firstで1ページの件数を指定し、前ページのendCursorを次回のafterに渡します。

ページネーションは、ただループを書く作業ではありません。途中で止まったときに、どこから再開するかを決める設計です。

要素 役割 実装で決めること
first 1回で取る件数 250固定にせず、costとレスポンスサイズで調整する
after 次ページの開始位置 前回保存したendCursorを渡す
nodes 取得対象の配列 cursorが不要な一覧処理では読みやすい
edges nodeとcursorの組 レコード単位のcursorやデバッグが必要な場合に使う
pageInfo.hasNextPage 次ページの有無 falseになるまで処理する
pageInfo.endCursor 次ページ用cursor バッチログに保存し、再開地点に使う

実務では、cursorだけでなく検索条件も保存します。query: "updated_at:>=2026-07-01"のような条件で回したバッチなら、再実行時に同じ条件、同じoperation、同じvariablesで再開できるようにします。

処理 cursor保存単位 注意点
商品全件棚卸し operation名、検索条件、endCursor、処理済み件数 商品更新が並行して起きるため、必要なら取得時点を固定する
注文差分連携 対象期間、注文ステータス、endCursor、最後に成功したOrder ID 同じ注文を二重送信しない処理済みキーが必要
メタフィールド補正 owner type、namespace/key、対象ID、成功/失敗 失敗したownerだけ再実行できるようにする
管理画面の一覧 ページごとのcursor ユーザー操作なのでレスポンス時間を優先する

first: 250で常に最大件数を取る設計は、分かりやすい一方で危険です。深いネストや重いfieldsがあると、件数を増やした分だけcostとレスポンスサイズが増えます。まず小さく試し、requestedQueryCostactualQueryCostを見て調整します。

query costをログで見る

Shopify公式のShopify API limitsでは、GraphQL Admin APIはcalculated query costで制限され、レスポンスのextensions.costrequestedQueryCostactualQueryCostthrottleStatusが返ると説明されています。さらに、Shopify-GraphQL-Cost-Debug=1ヘッダーを付けると、fieldごとのcost内訳を確認できます。

cost設計で見るべきものは、HTTPステータスだけではありません。

項目 意味 見る理由
requestedQueryCost Shopifyが実行前に見積もったcost クエリが重すぎるかを事前に判断する
actualQueryCost 実際の結果に応じたcost 検索結果が少ない場合など、実行後の実コストを見る
maximumAvailable 利用可能なcostバケット上限 ストア/アプリの上限感を把握する
currentlyAvailable 現在使える残量 次のリクエストを待つべきか判断する
restoreRate 1秒あたりの回復量 バッチの待機時間や並列数を決める
Cost-Debug fields どのfieldが重いか 深いネストや不要fieldを削る根拠にする

ログには、operation名、variables、対象件数、requested/actual cost、throttleStatusを残します。これがないと、本番で遅くなったときに「Shopifyが遅い」のか「自分たちのqueryが重い」のか切り分けられません。

cost超過時の対応は、待つだけでは不十分です。

症状 まず見るもの 設計変更
requested costが高い Cost-Debugのfield別内訳 不要fieldsを削る、ネストを分ける
actual costが高い 取得件数、検索条件、connection firstを下げる、条件を絞る
currentlyAvailableが不足 throttleStatus キュー化し、restoreRateに合わせて待機する
夜間バッチが終わらない 総件数、1ページcost、再試行回数 差分化またはBulk Operationsへ切り替える
管理画面操作が遅い レスポンス時間、同時実行 画面用queryとバッチ用queryを分ける

mutationはuserErrorsまで成功判定に入れる

GraphQL mutationは、HTTPとして成功しても、payload内のuserErrorsで業務エラーを返すことがあります。したがって、200 OKだけで成功扱いしてはいけません。

メタフィールド更新を例にすると、variablesでownerId、namespace、key、type、valueを渡し、戻り値で更新されたmetafieldsとuserErrorsを確認します。

mutation SetProductIntegrationMetafields($metafields: [MetafieldsSetInput!]!) {
  metafieldsSet(metafields: $metafields) {
    metafields {
      id
      namespace
      key
      value
    }
    userErrors {
      field
      message
      code
    }
  }
}

mutation設計では、成功/失敗を次の粒度で見ます。

観点 設計内容 ログに残すもの
入力単位 1件ずつか、複数件まとめるか ownerId、namespace/key、外部キー
成功判定 payloadの更新結果とuserErrorsを見る mutation名、成功件数、失敗件数
部分失敗 失敗した入力だけ再実行できるか field、message、code、該当input
冪等性 同じmutationを再実行しても結果が壊れないか idempotency key、外部連携ID、処理済み状態
手動補正 人が直すべきエラーを分けるか エラー分類、担当、対応メモ

在庫や注文連携では、差分加算のようなmutationを無造作に再実行すると危険です。再実行しても同じ結果になる処理、または再実行前に最新状態をqueryで取り直してから判断する処理にします。

再実行できるoperation名とvariablesにする

GraphQLの再実行性は、ログ設計で決まります。どのquery/mutationを、どのvariablesで、どのshopに、どのAPI versionで投げたかが残っていなければ、再実行はできません。

ログ項目 queryで必要 mutationで必要 理由
shop domain 必要 必要 複数ストアで取り違えない
API version 必要 必要 version差分を切り分ける
operation name 必要 必要 どの処理か分かる
variables 必要 必要 同じ条件で再実行する
cursor 一覧queryで必要 通常は不要 途中再開に使う
global ID 対象queryで必要 必須 対象を一意に特定する
requested/actual cost 必要 必要 重い処理を見直す
userErrors 不要 必須 失敗理由と再実行可否を見る
外部連携ID 場合による 必須 二重送信や二重更新を避ける

operation名を付けずに匿名queryを投げ続けると、ログ上で何が起きたか追いにくくなります。ProductsForDeltaSyncOrdersForFulfillmentExportSetProductIntegrationMetafieldsのように、業務処理が分かる名前を付けます。

Bulk Operationsへ切り替える基準

Shopify公式のAPI limitsでは、大量データの取得には単発queryではなくBulk Operationsを使うべきと説明されています。Bulk Operationsは、大量データを非同期で処理するための仕組みです。通常のqueryと同じ感覚で「遅くなったらBulkにする」ではなく、最初から判断基準を決めておきます。

処理 通常Queryが向く Bulk Operationsが向く
管理画面で1商品を表示 向く 向かない
Webhook後に1注文を取り直す 向く 向かない
更新日で絞った数百件の商品差分 向く 条件次第
数万商品の初期同期 重くなりやすい 向く
過去数年の注文を分析DBへ投入 重くなりやすい 向く
全メタフィールド棚卸し 件数次第 向く
ユーザー操作に即時結果を返す処理 向く 向かない

Bulk Operationsを使う場合も、設計は必要です。

設計項目 決めること
開始条件 初期同期、月次棚卸し、過去注文投入など、通常処理と分ける
query内容 Bulk用にfieldsを絞り、画面用queryを流用しない
完了検知 Webhookまたはポーリングで状態を確認する
結果処理 JSONLを1行ずつ処理し、メモリに全件載せない
保存先 取得ファイル、処理済みID、エラー行、再実行キーを保存する
再実行 前回結果の破棄、差分再取得、部分再処理を分ける

Bulk Operationsは「重いqueryの逃げ道」ではなく、通常APIとは別の処理方式です。即時性が必要な処理、1件ずつ最新状態を見たい処理、mutationで細かく書き戻す処理とは分けて設計します。

実装前チェックリスト

最後に、Shopify GraphQLのquery/mutationを本番に入れる前のチェックリストを置きます。

チェック項目 確認すること
スキーマ探索 GraphiQLでQueryRoot、対象型、Connection、Input、Payloadを確認したか
operation名 匿名queryではなく、業務処理名が分かる名前を付けたか
variables ID、cursor、件数、検索条件をvariablesに出したか
fields 連携先や画面が使うfieldsだけに絞ったか
global ID Shopify global IDと外部キーの対応を保存するか
pagination firstafterpageInfo.endCursor、再開方法を決めたか
cost requested/actual cost、throttleStatus、Cost-Debugを見るログがあるか
mutation userErrorsを成功判定に含めたか
再実行 失敗した対象だけ再実行できるoperation/variables/ログになっているか
Bulk判断 通常Query、差分Query、Bulk Operationsの切替基準を決めたか

このチェックリストが埋まっていれば、Shopify GraphQLは「書けるクエリ」から「運用できるクエリ」に近づきます。

まとめ

Shopify GraphQLは、必要なfieldsだけを取れる強力なAPIです。しかし、本番運用ではそれだけでは足りません。

GraphiQLでスキーマを探索し、QueryRootから入口を選び、query/mutationを業務単位に分ける。variablesとglobal IDで再実行できる形にし、products/orders/metafieldsのfieldsを最小化する。connections、nodes、edges、pageInfo、firstafterで途中再開できるページネーションを設計する。requestedQueryCostactualQueryCostthrottleStatus、Cost-Debugをログに残し、重いqueryは分割する。mutationではuserErrorsを見て、部分失敗と冪等性を扱う。大量取得は通常queryで押し切らず、Bulk Operationsへ切り替える。

Bitlightでは、ShopifyのGraphQL Admin APIを単なる開発手段ではなく、商品、注文、メタフィールド、外部DB、運用ログ、再実行まで含めた業務連携として設計します。GraphQLのサンプルを動かす前に、過取得・cost超過・再実行不能を避ける設計に落とし込むことが、長く保守できるShopify連携の第一歩です。

Shopify運用設計支援

Shopify GraphQLを、壊れにくい業務連携として実装します

Admin API、Bulk Operations、Webhook、外部DBの境界を整理し、過取得・cost超過・再実行不能を避けるShopify連携を設計します。

著者
守高 成悟
守高 成悟

代表取締役 CEO

千葉県出身。10歳の頃からプログラミングを始め、ゲーム、Webサイト、ロボット、スマホアプリなどを制作。大阪大学基礎工学部情報科学科で情報工学と統計学を学び、大学時代はAIを研究。大学在学中にWeb広告代理店でのインターンや人材系Webサービスの立ち上げを経験し、卒業後はフリーランスエンジニアとしてGISシステム、データ基盤構築、Webシステムの開発に従事。10年以上のWebアプリ開発・データ分析経験を基に、2023年9月に株式会社ビットライトを設立し、現場業務の仕組み化からデータ基盤構築、データ活用支援までを一気通貫で支援。

運営会社
株式会社ビットライト
株式会社ビットライト

顧客が本当に必要だった価値を、実装する。

現場業務の仕組み化からデータ基盤構築、データ活用支援までを一気通貫で支援しています。

コーポレートサイトを見る
Shopify設計相談

商品データ・在庫・連携範囲を相談できます

既存Shopifyの見直し、在庫・受注・会計・CRM・外部アプリ連携まで、運用に合わせた設計範囲を整理します。