Skip to main content

Shopify のための GraphQL エッセンシャル

GraphQL は Shopify の API とのコミュニケーションにおける主要な言語です。これまで REST API のみを使用してきた方にとって、GraphQL は異なる感覚があるでしょう -- 必要なデータの形状を正確に記述すると、サーバーはまさにその形状を返します。オーバーフェッチもアンダーフェッチもなく、複数のリクエストを連鎖させる必要もありません。このレッスンでは、GraphQL をゼロから、毎日使用する Shopify 固有のパターンとともに教えます。

REST よりも GraphQL を選ぶ理由

商品をバリアント、画像、メタフィールドとともに取得することを考えてみましょう。REST では以下が必要になるかもしれません:

GET /admin/api/2026-01/products/123.json          → 商品データ
GET /admin/api/2026-01/products/123/variants.json → バリアントデータ
GET /admin/api/2026-01/products/123/images.json → 画像データ
GET /admin/api/2026-01/products/123/metafields.json → メタフィールドデータ

これは4つの HTTP リクエストであり、それぞれ不要なフィールドを返す可能性があります。GraphQL では:

query {
product(id: "gid://shopify/Product/123") {
title
status
variants(first: 10) {
edges {
node {
title
price
inventoryQuantity
}
}
}
images(first: 5) {
edges {
node {
url
altText
}
}
}
metafields(first: 10) {
edges {
node {
namespace
key
value
}
}
}
}
}

1つのリクエスト。必要なフィールドだけ。これが Shopify が GraphQL に全面移行している理由です。

クエリ:データの読み取り

クエリは Shopify からデータを読み取る方法です。すべてのクエリはルートフィールドから始まり、データグラフを掘り下げていきます。

基本的なクエリ構造

query ProductList {
products(first: 10, query: "status:active") {
edges {
node {
id
title
handle
status
totalInventory
priceRangeV2 {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

主要な概念:

  • オペレーション名ProductList):オプションですが推奨 -- デバッグとロギングに役立ちます
  • 引数first: 10, query: "status:active"):結果のフィルタリングとページネーション
  • セレクションセット:返してほしいネストされたフィールド
  • Edge と Node:Shopify はリストに Relay コネクションパターンを使用
  • pageInfo:カーソルベースページングのページネーションメタデータ

変数の使用

値をクエリ文字列に直接補間しないでください。代わりに変数を使用します:

query ProductById($id: ID!) {
product(id: $id) {
title
description
vendor
productType
tags
createdAt
}
}
{
"variables": {
"id": "gid://shopify/Product/7654321"
}
}
常に変数を使用する

変数は GraphQL インジェクション攻撃を防ぎ、クエリキャッシュを有効にし、コードをクリーンにします。Remix テンプレートの Shopify Admin GraphQL クライアントはネイティブに変数をサポートしています。

一般的なクエリパターン

ラインアイテムとフルフィルメントステータスを含む注文の取得:

query RecentOrders($first: Int!) {
orders(first: $first, sortKey: CREATED_AT, reverse: true) {
edges {
node {
id
name
displayFinancialStatus
displayFulfillmentStatus
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
lineItems(first: 50) {
edges {
node {
title
quantity
variant {
sku
}
}
}
}
customer {
displayName
email
}
}
}
}
}

フィルター付きの顧客検索:

query SearchCustomers($query: String!) {
customers(first: 25, query: $query) {
edges {
node {
id
displayName
email
ordersCount
totalSpent
tags
addresses {
city
province
country
}
}
}
}
}

ロケーション間の在庫レベル確認:

query InventoryLevels($variantId: ID!) {
productVariant(id: $variantId) {
title
sku
inventoryItem {
inventoryLevels(first: 10) {
edges {
node {
location {
name
}
quantities(names: ["available", "committed", "on_hand"]) {
name
quantity
}
}
}
}
}
}
}

ミューテーション:データの書き込み

ミューテーションはデータを変更します。Shopify の API では一貫したパターンに従います:input オブジェクトを渡すと、ミューテーションは変更されたリソースとユーザーエラーを返します。

商品の作成

mutation CreateProduct($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
handle
status
variants(first: 5) {
edges {
node {
id
price
}
}
}
}
userErrors {
field
message
}
}
}
{
"variables": {
"input": {
"title": "Premium Widget",
"descriptionHtml": "<p>A high-quality widget for discerning customers.</p>",
"vendor": "WidgetCo",
"productType": "Widgets",
"tags": ["premium", "new-arrival"],
"status": "DRAFT",
"variants": [
{
"price": "29.99",
"sku": "WIDGET-001",
"inventoryQuantities": [
{
"availableQuantity": 100,
"locationId": "gid://shopify/Location/1"
}
]
}
]
}
}
}
常に userErrors を確認する

Shopify のミューテーションは例外をスローする代わりに userErrors 配列を返します。ミューテーションは HTTP 200 で成功した GraphQL レスポンスを返しつつも userErrors を含む場合があります。オペレーションが成功したと見なす前に、常にこの配列を確認してください。

注文タグの更新

mutation AddOrderTags($id: ID!, $tags: [String!]!) {
tagsAdd(id: $id, tags: $tags) {
node {
... on Order {
id
tags
}
}
userErrors {
field
message
}
}
}

メタフィールドミューテーション

mutation SetMetafields($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields {
id
namespace
key
value
}
userErrors {
field
message
}
}
}
{
"variables": {
"metafields": [
{
"ownerId": "gid://shopify/Product/123",
"namespace": "custom",
"key": "warranty_years",
"value": "3",
"type": "number_integer"
}
]
}
}

サブスクリプション:リアルタイム更新

GraphQL サブスクリプションにより、アプリは Webhook を介してリアルタイム更新を受信できます。Shopify は WebSocket ベースのサブスクリプションをサポートしていませんが、GraphQL を通じて登録される Webhook サブスクリプションを使用します:

mutation CreateWebhookSubscription {
webhookSubscriptionCreate(
topic: ORDERS_CREATE
webhookSubscription: {
callbackUrl: "https://your-app.com/webhooks/orders-create"
format: JSON
}
) {
webhookSubscription {
id
topic
endpoint {
... on WebhookHttpEndpoint {
callbackUrl
}
}
}
userErrors {
field
message
}
}
}

利用可能なトピックには ORDERS_CREATEPRODUCTS_UPDATECUSTOMERS_CREATEINVENTORY_LEVELS_UPDATE など、多数あります。

GraphQL エクスプローラー

Shopify はクエリをテストするためのインタラクティブな GraphQL エクスプローラーを提供しています:

  1. パートナーダッシュボードに移動
  2. アプリを選択し、API アクセスをクリック
  3. GraphiQL をクリックしてエクスプローラーを開く

または Shopify CLI を使用:

# 開発ストア用の GraphiQL を開く
shopify app dev --graphiql

エクスプローラーは以下を提供します:

  • スキーマドキュメント:すべての型、フィールド、引数を閲覧
  • オートコンプリート:入力時に候補を表示
  • クエリバリデーション:実行前にエラーをハイライト
  • 変数エディター:異なる入力でテスト
  • レスポンスビューアー:フォーマットされた JSON 出力
Claude Code を GraphQL エクスプローラーとして使用する

GraphiQL でクエリを手動で書く代わりに、Claude Code に聞いてみましょう:「最新の注文5件をラインアイテム、顧客メール、フルフィルメントステータスとともに取得する GraphQL クエリを書いてください。」Claude Code は Shopify のスキーマを理解しており、正確で効率的なクエリを生成します。

一括オペレーション

数千または数百万のレコードを処理する必要がある場合、個別のクエリでは遅すぎます。Shopify の一括オペレーション API を使用すると、データセット全体に対してクエリを非同期で実行できます。

mutation BulkExportProducts {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id
title
handle
status
variants {
edges {
node {
id
sku
price
inventoryQuantity
}
}
}
}
}
}
}
"""
) {
bulkOperation {
id
status
url
}
userErrors {
field
message
}
}
}

一括オペレーションは非同期で実行されます。完了をポーリングします:

query BulkOperationStatus {
currentBulkOperation {
id
status
objectCount
url
errorCode
fileSize
}
}

statusCOMPLETED になると、url フィールドにすべての結果を含む JSONL ファイルへのリンクが含まれます。各行は JSON オブジェクトで、ネストされたオブジェクトは __parentId フィールドを通じて親を参照します。

{"id":"gid://shopify/Product/1","title":"Widget A","handle":"widget-a","status":"ACTIVE"}
{"id":"gid://shopify/ProductVariant/10","sku":"WA-001","price":"29.99","__parentId":"gid://shopify/Product/1"}
{"id":"gid://shopify/Product/2","title":"Widget B","handle":"widget-b","status":"DRAFT"}
{"id":"gid://shopify/ProductVariant/20","sku":"WB-001","price":"19.99","__parentId":"gid://shopify/Product/2"}
一括オペレーションの使用タイミング

数百件以上のレコードをエクスポートまたは処理する必要がある場合に一括オペレーションを使用します。一般的なユースケース:データ移行、レポート作成、外部システムとの同期、初期データロード。一括オペレーションは通常のレート制限の対象外です。

エラーハンドリング

Shopify の GraphQL エラーは2つの形式で発生します:

1. GraphQL エラー(クエリレベル)

クエリ自体の問題を示します -- 構文エラー、無効なフィールド、認証の失敗:

{
"errors": [
{
"message": "Field 'nonExistentField' doesn't exist on type 'Product'",
"locations": [{ "line": 3, "column": 5 }]
}
]
}

2. ユーザーエラー(ビジネスロジック)

有効なクエリがビジネスルールに失敗したことを示します:

{
"data": {
"productCreate": {
"product": null,
"userErrors": [
{
"field": ["input", "title"],
"message": "Title can't be blank"
}
]
}
}
}

両方のタイプを常に処理します:

const response = await admin.graphql(query, { variables });
const { data, errors } = await response.json();

// クエリレベルのエラーを確認
if (errors) {
console.error("GraphQL errors:", errors);
throw new Error(errors[0].message);
}

// ビジネスロジックのエラーを確認
const result = data.productCreate;
if (result.userErrors.length > 0) {
console.error("User errors:", result.userErrors);
throw new Error(result.userErrors[0].message);
}

// 成功
return result.product;

重要なポイント

  • GraphQL を使用すると、1回のリクエストで必要なデータを正確に取得できます
  • 安全性とキャッシュのために、文字列補間ではなく変数を使用してください
  • ミューテーションレスポンスでは常に userErrors を確認してください
  • 大規模なデータセット(数百件以上のレコード)には一括オペレーションを使用してください
  • Relay コネクションパターン(edges/nodes/pageInfo)はすべてのリストに使用されます
  • Claude Code は自然言語の説明から Shopify GraphQL クエリを生成できます

次は、Shopify の Online Store 2.0 テーマを動かすテンプレート言語である Liquid を探ります。