ShopifyAPI::GraphQL::Metafields
User-friendly metafield wrapper around Shopify's GraphQL Admin API with built-in retry, batching, and error handling.
Installation
bundle add shopify_api-graphql-metafieldsIf bundler is not being used to manage dependencies:
gem install shopify_api-graphql-metafieldsUsage
ShopifyAPI::GraphQL::Metafields supports CRUD operations on Metafields and Metafield Definitions, and supports setting metafields in bulk.
Metafields
Upsert Metafields
You can upsert a single metafield or multiple metafields:
require "shopify_api/graphql/metafields"
metafields = [
{
:owner_id => "gid://shopify/Product/123",
:namespace => "something",
:key => "foo",
# Not required if there's a metafield definition
:type => "number_integer",
:value => "123"
},
{
:owner_id => "gid://shopify/Shop/123",
:namespace => "whatever",
:key => "bar",
:type => "json",
:value => JSON.dump(:a => 123, :b => "bee")
}
]
mf = ShopifyAPI::GraphQL::Metafields.new(shop, token)
begin
results = mf.set(metafields)
results.each do |result|
# result is a Hash with underscored Symbol keys
p result.slice(:id, :value)
end
rescue ShopifyAPI::GraphQL::Metafields::Error => e
p e
endTo override the fields returned see Field Selection.
Delete Metafields
metafields = [
{
:owner_id => "gid://shopify/Product/123",
:namespace => "something",
:key => "foo"
},
{
:owner_id => "gid://shopify/Shop/123",
:namespace => "whatever",
:key => "bar"
}
]
mf = ShopifyAPI::GraphQL::Metafields.new(shop, token)
begin
results = mf.delete(metafields)
results.each do |result|
# result is a Hash
p result.slice(:key, :namespace)
end
rescue ShopifyAPI::GraphQL::Metafields::Error => e
p e
endTo override the fields returned see Field Selection.
Field Selection
Use the :fields option to select what fields are returned. This accepts an Array of field names:
mf = ShopifyAPI::GraphQL::Metafields.new(shop, token)
mf.set(metafield, :fields => %w[compare_digest legacy_resource_id description])Or a String containing literal GraphQL fields:
mf.set(metafield, :fields => "legacyResourceId constraints { key }")Setting Metafields in Bulk
require "shopify_api/graphql/metafields"
set = ShopifyAPI::GraphQL::Metafields::Batch::Set.new(shop, token)
set << metafield1
set << metafield2
# ...
set << metafield100
begin
result = set.flush!
rescue ShopifyAPI::GraphQL::Metafields::Batch::Error => e
p "Bulk set failed: #{e}"
if e.results.any?
p "We did have _some_ success:"
pp e.results
end
if e.errors.any?
p "We encountered some errors:"
pp e.errors
end
if e.user_errors.any?
p "We encountered some user errors:"
pp e.user_errors
end
endNote that this does not use the Shopify Bulk API. If you want to use the Bulk API for metafields or other operations
see ShopifyAPI::GraphQL::Bulk.
Once #flush! is called worker threads (if needed) will be spawned and metafields will be set 25 at a time, per Shopify's Mutations.metafieldsSet call limit.
You can control the number of threads via the :worker_count option:
set = ShopifyAPI::GraphQL::Metafields::Bulk.new(shop, token, :worker_count => 8)Workers are spawned based on the batch size. If you have 50 metafields only 2 workers will be spawned. If you have 25 or less only no workers are spawned.
The default number of workers is 4.
The return value is an Array of results like one gets with Metafield#set. The indices will align with the input metafields' indices.
A nil value means that position errored.
Deleting Metafields in Bulk
TODO!
If you need this you can use Shopify's Bulk API via ShopifyAPI::GraphQL::Bulk.
Metafield Definitions
require "shopify_api/graphql/metafields"
dfn = ShopifyAPI::GraphQL::Metafields::Definition.new(shop, token)
result = dfn.create(
:name => "My amaaaazing definition",
:namespace => "sshaw",
:key => "ingredients",
:description => "A product's ingredients",
:type => "multi_line_text_field",
:owner_type => "PRODUCT"
)
id = result.dig(:created_definition, :id)
result = dfn.update(
:namespace => "sshaw",
:key => "ingredients",
:owner_type => "PRODUCT",
:description => "A products SUPER SECRET ingredients"
)
# More CRUD operations...Field Selection
Similar to other metafield operation you can specify the returned fields:
id = { :namespace => "sshaw", :key => "ingredients", :owner_type => "PRODUCT" }
dfn.get(id, :fields => %w[id pinned_position])
# Or, specify a String:
dfn.get(id, :fields => "id pinnedPosition constraints { key value }")Why Use This Instead of Shopify's API Client?
- Easy-to-use
- Built-in retry
- Batching
- Lightweight
- You can use :snake_case hash keys
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
Development
Shopify Development Tools is used to import the product fixtures and retrieve values. The sdt executable must be in your path.
A Shopify test store is required. cp .env.template .env and fill-in the values. Requests will be made to the given store. The token needs permission to create metafields and metafield definitions.
See Also
- Shopify Dev Tools - Command-line program to assist with the development and/or maintenance of Shopify apps and stores
-
ShopifyAPI::GraphQL::Tiny- Lightweight, no-nonsense, Shopify GraphQL Admin API client with built-in pagination and retry -
ShopifyAPI::GraphQL::Request- A higher-level wrapper aroundTinywith improved exception handling and:snake_casehash key conversion -
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