Closed5

cURLでShopify GraphQL API入門

HAZIHAZI

GraphQLライブラリなどを使わずcURLでShopify GraphQL APIを叩く場合に必要な情報などをまとめています。
FileMakerから使う場合を主に想定してますが、他でも同様だと思うので参考にしてください。

学び始め

まず、オフィシャルのブログ記事を読む GraphQLを始めよう - Shopify

動画もある(字幕はちゃんと日本語に翻訳されてる)
https://youtu.be/ARgQ4oK0Mz8

ブログの中で紹介されているShopify Admin API GraphiQL explorer をインストールする Shopify Admin API GraphiQL explorer
基本的にQueryを作る際にはこれを使ってドキュメントと睨めっこしなが作ることになる。

開発テスト用の店舗にインストールして使いましょう。

GraphQLの事態の詳細な仕様が知りたい場合には GraphQL | A query language for your API などを確認すると良い。

Shopify Admin API GraphiQL explorer の注意点。

Shopify Admin API GraphiQL explorerはShopifyオリジナルアプリなためアクセス権限が特殊。

Shopify PlusじゃないとアクセスできないようなAPIにもアクセスできてしまうので、自前のアクセストークンを使ってAPIを叩いた際に権限関連のエラーが出た際にはドキュメントを参照し、契約プランで使えるAPIかを確認するようにしよう。

例: productPublish
Requires write_publications access scope. This scope is currently available only to private apps installed on Shopify Plus stores. Contact Shopify Partner Support to enable the scope for your app.

多分、Development StoreでShopify Plusストア同等の権限のテストができるようにしてあるんじゃないかな?

HAZIHAZI

ざっくりGraphQL

GraphQLを始めよう - Shopify を読んだ想定で進めます。

なんとなーく GraphQL をどう扱えばいいのかわかったと思うので、GraphQLの一番上位部であるqueryvariablesの使い分けや役割などをざっくり解説。

queryvariables

GraphQLのデータには大きく別けてquery 部と variables 部の2つがある。

これはSQLの "クエリー" と "引数" に似たようなイメージで、クエリーに変数を渡したい場合、SQLのクエリーに ? を使うようなパターンでGraphQLでは variables もあわせて使う。そうじゃない場合は query のみで完結できる。っていうざっくりの解釈でいいかと。

また、query に定数としてidなどの値を埋め込むことも可能で、その場合も同様に variables は不要。

※Shopify Admin API GraphiQL explorerで variablesを指定する際は左下に「QUERY VARIABLES」というタブが隠れてるのでクリックして表示させてあげないと書けないので注意。

query のみで更新

よくない例にはなるが query のみで商品名を更新する場合は下記のような形になる

Query
mutation{
  productUpdate(input: {
    id: "gid://shopify/Product/1111111",
    title: "New product name"
  }){
    product {
      id
      title
    }
  }
}
  1. productUpdate の引数 input: に必須である id: と変更したい title: を渡すことで、商品ページのタイトルを書き換えられる。
  2. product { ... } は実行後の戻り値はこんなデータが欲しいですという宣言

queryvariables で更新

queryvariables を使って更新する場合は下記のような形になる

Query
mutation($productInput: ProductInput!) {
  productUpdate(input: $productInput){
    product {
      id
      title
    }
  }
}
Variables
{
  "productInput": {
  	"id": "gid://shopify/Product/1111111",
  	"title": "New product name"
  }
}

ブログを読んだだけだと良くわからないと思うので、簡単に解説。

  1. mutation($productInput: ProductInput!)
  • $productInput は変数名、Variablesの第一階層の名前 "productInput" を指す
  • ProductInput! は変数の中身の "型" を表してる。どの形式のJSONを引数に入れますよという宣言(詳細は後述)。
  1. productUpdate(input: $productInput)
  • (input: $productInput)productUpdate の引数名 inputvariables"productInput" のオブジェクトを渡すという宣言
  • query のみの例の2行目〜5行目に該当する部分
  1. product { ... } は実行後の戻り値はこんなデータが欲しいですという宣言

queryvariables に別けて実行する意味

queryvariables を別ける意味はメンテナンス性やセキュリティ問題などいろいろな理由があると思うが、使う側の、特にGraphQLライブラリを使わずcURLで直接GraphQLを実行する側からすると、Queryは特殊なフォーマットなのでできるだけ頻繁に変更を加えたり、動的に生成したくない。対してVariablesはJSONなので動的に生成しやすいという側面がある。

なのでメンテナンス性を維持するためにもしっかりと使い分けよう。

"型" とは

型はデータがどんな形式でやり取りするべきなのかを事前に宣言したもの。
Shopifyが事前にいろいろな型を作っており、それに合わせたJSONやQueryを指定しないとエラーになる。

型名は基本的に大文字から始まるパスカルケースで書かれているので、ドキュメントを見るとわかると思う。

今回の例だと productUpdate mutation の引数部分を見ると productUpdate(input:ProductInput!) と書かれており、引数 input: の型が ProductInput! であることがわかる。

具体的な型の指すフォーマット内容が知りたい場合は、隣のアンダーライン付きの ProductInput! がリンクになってるので、飛ぶとどんなフォーマットかわかる。

こういったものを参考に、mutation 引数の型と productUpdate の引数の型が一致するように宣言していけばOK

HAZIHAZI

GraphQLをcURLで使う

GraphQLをcURLから使う例

Query実行(データ取得)

最初の5商品の情報を取得するQuery

Query
{
  products(first: 5) {
    edges { node {
      id
      title
      vendor
      onlineStoreUrl
      onlineStorePreviewUrl
      productType
      status
      handle
  } }
}

cURLでの実行例

curl -X "POST" "https://{StoreName}.myshopify.com/admin/api/{Version}/graphql.json" \
     -H 'X-Shopify-Access-Token: {Token}' \
     -H 'Content-Type: application/json' \
     -d $'{
          "query": "{products(first:5){edges{node{id title vendor onlineStoreUrl onlineStorePreviewUrl productType status handle}}}}"
        }'

mutation実行(更新・作成)

商品名を変更するQuery

Query
mutation($input: ProductInput!) {
  productUpdate(input: $input){
    product {
      id
      title
    }
  }
}

引数

Variables
{"input":{"id":"gid:\/\/shopify\/Product\/1111111","title":"New product name"}}

cURLでの実行例

curl -X "POST" "https://{StoreName}.myshopify.com/admin/api/{Version}/graphql.json" \
     -H 'X-Shopify-Access-Token: {Token}' \
     -H 'Content-Type: application/json' \
     -d $'{
  "query": "mutation($input:ProductInput!){productUpdate(input:$input){product{id title}}}",
  "variables": {
    "input": {
      "id": "gid://shopify/Product/1111111",
      "title": "New product name"
    }
  }
}'
HAZIHAZI

画像やビデオをアップロードして商品に関連づける

Manage product media with the GraphQL Admin API - Shopify に書いてあるとおり。

ざっくりだけ解説。

GraphQLは商品更新時ついでにバリエーションや在庫の情報を更新できる優れたフォーマットだが、商品更新時や作成時に画像をアップロードすることはできない。

下記のようにしてアップロードと紐付けを行う。

  1. mutation generateStagedUploads を使って、まずアップロードURLを発行する。
  2. 戻り値のアップロードURL(data.stagedUploadsCreate.stagedTargets[].url)とパラメータを元に画像や動画をアップロードする。
  3. resourceUrl(data.stagedUploadsCreate.stagedTargets[].resourceUrl)を使って、商品に画像を紐づける。

という3ステップが必要。

アップロードURLの仕様

アップロードURL(data.stagedUploadsCreate.stagedTargets[].url)はちょっと癖のある仕様

  • 一定時間が経過するとアップロードできなくなる(実行してみたところ15分)
  • ダウンロードURLとしては使えない
  • 同じURLに複数回アップロードし直せる(上書きされる)
  • resourceUrlもダウンロードURLとして使えない(あくまで商品紐付け用のID的なもの?)
  • resourceUrlは複数の商品に使いまわせる

画像はAmazon S3に直接アップしてるので、これはShopifyの仕様というか主にS3の仕様。

アップロード用URLはまとめて複数発行できるので、まとめて発行して1つずつ画像をアップロードして、最後にまとめて商品と紐づける。という流れが最小アクセス回数で画像を商品に紐づける方法っぽい。

画像を商品に紐づける際の注意点

  • 画像登録時は必ず戻り値に画像の id を含めるようにし、アップロードした画像情報とともにidをDBに保存しないと、後でAltや順番を変更したいとき困るので注意。
  • productUpdate mutationProductInput.images.srcresourceUrl を指定することで、商品画像の登録は可能。
    • ただし、画像登録に関するエラーが返ってこないので登録時にはあまり使わない方が良さそう。
    • 画像のIDを指定せずにsrcにresouceUrlを指定する形で順番やAltの変更行おうとすると、一旦全ての画像を削除して登録し直すことになるため時間がかかるのでおすすめしない。
    • 画像のIDを指定せずに https://cdn.shopify.com から始まるURLをsrcに指定して表示順やAltを更新したりすると、画像が消えることがある(IDがないので既存の画像を削除した後に登録し直すことになるため、画像再登録時に画像が消えておりダウンロードできずに結果消えたように見える。前述の通りエラーが出ない)。
    • なので、一度登録した画像の順番やAltを変更する際は id を使うようにする。
  • 画像追加専用の productAppendImages mutation を使用する方が新規登録は無難。
    • 画像登録時に src に無効なURLが含まれている場合はどは、ちゃんと userErrors にその情報が返ってくるようになっている。
    • 画像の追加のみが行えるので、配列に既存に画像を追加し忘れて消えてしまうということがない。
  • 画像の入れ替え、altの変更のみが行いたい場合には productImageUpdate mutation を使うのが無難
  • 画像の削除のみが行いたい場合には productDeleteImages mutation を使うのが無難
HAZIHAZI

在庫の更新を行う

Manage product inventory with the Admin API に書いてあるとおり。

補足として

基本的には在庫の更新を行う前に商品バリエーションの登録を行い、InventoryItemId をDBに保存しておく必要がある。

在庫数は InventoryItem の下のレベルのモデル InventoryLeve(ロケーション別在庫の情報)に保存されている。
ただ、在庫数の更新には必ずしも InventoryLevelId が必要なわけではなく、LocationIdInventoryItemId を使って更新できるのでよく見極める必要がある。

また、GraphQLでは在庫数の更新は相対値で行う必要がある。
絶対値で更新する場合にはREST APIを使うしかないようだ。


参考: [GraphQL] New Mutation For Bulk Inventory Adjustment At A Single Location

新規登録に使えるAPIは

などがある。
ただ、inventoryActivateを使って新しいロケーションに在庫を配置しようとしても、在庫を増やすことができない。もしかしたら、InventoryLevelの作成ではなくVariantの作成時にしか絶対値の在庫数がセットできないのかもしれない。

ProductVariantInput.inventoryQuantities [InventoryLevelInput!]
Create only field. The inventory quantities at each location where the variant is stocked.

在庫数を相対値(availableDelta)で更新する場合使えるAPIは、InventoryAdjustQuantityInputInventoryAdjustItemInput を使ったAPIで、

などがある。

これらの情報を見るに、Shopifyは在庫数の絶対値での更新をAPI経由では行わせたくないようだ。

理由としては

  • 在庫数の管理がそもそも相対値で行われていること
  • 絶対値でセットできると、タイミングによって在庫数のズレが発生すること
  • 絶対値での更新はDBへの負荷が大きいこと

などがあくまで予想だが背景としてはありそうだ。

なので、絶対値で更新したい場合は現在のShopifyの在庫数を取得し、差が発生している商品に対してのみ在庫数を相対値でアップロードするのが無難そう。

このスクラップは2022/07/25にクローズされました