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
end

Then 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}"
end

Subclasses 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 of TinyGID)
  • #gql - The underlying GraphQL client (an instance of ShopifyAPI::GraphQL::Tiny)

#execute and #paginate also:

  • Automatically retry failed or rate-limited requests
  • Accept snake_case Symbol keys
  • Return a Hash with snake_case Symbol keys
  • Raise a UserError when a mutation's response contains userErrors
  • Raise a NotFoundError when 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
end

Disabling 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
end

Making 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
end

And 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
end

More Info

For more information checkout the API docs

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