ShopifyAPI::GraphQL::Request
Small class to simplify the writing and handling of GraphQL queries and mutations for the Shopify Admin API. Comes with built-in retry, pagination, error handling, and more!
Usage
It's recommended to organize queries and mutations by subclassing ShopifyAPI::GraphQL::Request:
require "shopify_api/graphql/request"
class ShopifyProduct < ShopifyAPI::GraphQL::Request
# Define your queries/mutations
FIND =<<-GQL
query($id: ID!) {
product(id: $id) {
id
title
descriptionHtml
}
}
GQL
UPDATE =<<-GQL
mutation($product: ProductUpdateInput!) {
productUpdate(product: $product) {
product {
id
title
descriptionHtml
}
userErrors {
field
message
}
}
}
GQL
LIST <<-GQL
query($after: String) {
products(first: 25 after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
title
descriptionHtml
}
}
}
}
GQL
def find(id)
execute(FIND, :id => gid::Product(id)).dig(:data, :product)
end
def update(id, changes)
execute(UPDATE, :product => changes.merge(:id => gid::Product(id))).dig(:data, :product_update, :product)
end
def list
paginate(LIST) { |page| yield page.dig(:data, :products, :edges) }
end
endThen use it:
shopify = ShopifyProduct.new("a-shop", token)
begin
product = shopify.find(123)
p product[:id]
p product[:description_html]
rescue ShopifyAPI::GraphQL::Request::NotFoundError => e
warn "Product not found: #{e}"
end
begin
product = shopify.update(123, :description_html => "Something amaaaazing!")
p product[:id]
p product[:description_html]
rescue ShopifyAPI::GraphQL::Request::UserError => e
warn "User errors:"
e.errors { |err| warn err["field"] }
end
begin
shopify.list(123) do |node|
product = node[:node]
p product[:id]
p product[:description_html]
end
rescue ShopifyAPI::GraphQL::Request::NotFoundError => e
warn "Request failed: #{e}"
endSubclasses have access to the following methods:
-
#execute- Execute a query or mutation with the provided arguments -
#paginate- Execute a query with pagination; without a block a lazy enumerator (Enumerator::Lazy) is returned -
#gid- Used for Global ID manipulation (an instance ofTinyGID) -
#gql- The underlying GraphQL client (an instance ofShopifyAPI::GraphQL::Tiny)
#execute and #paginate also:
- Automatically retry failed or rate-limited requests
- Accept
snake_caseSymbolkeys - Return a
Hashwithsnake_caseSymbolkeys - Raise a
UserErrorwhen a mutation's response containsuserErrors - Raise a
NotFoundErrorwhen a query's result cannot be found
Both of these are small wrappers around the equivalent methods on ShopifyAPI::GraphQL::Tiny.
For more information see its documentation on retries.
With the exception of retry, most defaults can be disabled per instance or per execution:
class ShopifyProduct < ShopifyAPI::GraphQL::Request
def initialize(shop, token)
super(shop, token, :raise_if_not_found => false, :raise_if_user_errors => false, :snake_case => false)
end
def find(gid)
execute(QUERY, :raise_if_not_found => true)
end
endDisabling retry must be done per instance.
Setting the GraphQL API Version
Pass the desired version to Request's constructor:
class ShopifyProduct < ShopifyAPI::GraphQL::Request
def initialize(shop, token)
super(shop, token, :version => "2026-01")
end
endMaking Requests Without Subclassing
Of course you can make requests directly on an instance of ShopifyAPI::GraphQL::Request:
require "shopify_api/graphql/request"
request = ShopifyAPI::GraphQL::Request.new("a-shop", token)
begin
product = request.execute(query, :id => "gid://shopify/Product/123").dig(:data, :product)
p product[:title]
p product[:description_html]
rescue ShopifyAPI::GraphQL::Request::NotFoundError => e
p e
endAnd mutations:
begin
product = request.execute(mutation, :id => "gid://shopify/Product/123", :title => "Foo Hoo!").dig(:data, :product)
rescue ShopifyAPI::GraphQL::Request::UserError => e
p e
endMore Info
For more information checkout the API docs
Why Use This Instead of Shopify's API Client?
- Easy-to-use
- Built-in retry
- Built-in pagination
- Improved exception handling
- You can use
:snake_casehash keys - Lightweight
Overall, Shopify's API client is bloated trash that will give you development headaches and long-term maintenance nightmares.
We used to use it, staring way back in 2015, but eventually had to pivot away from their Ruby libraries due to developer frustration and high maintenance cost (and don't get us started on the ShopifyApp gem!@#).
For more information see: Shopify/shopify-api-ruby#1181
Testing
cp env.template .env and fill-in .env with the missing values. This requires a Shopify store.
See Also
- Shopify Dev Tools - Command-line program to assist with the development and/or maintenance of Shopify apps and stores
- Shopify ID Export - Dump Shopify product and variant IDs —along with other identifiers— to a CSV or JSON file
-
ShopifyAPI::GraphQL::Tiny- Lightweight, no-nonsense, Shopify GraphQL Admin API client with built-in pagination and retry -
TinyGID- Build Global ID (gid://) URI strings from scalar values
License
The gem is available as open source under the terms of the MIT License.
Made by ScreenStaring