{"id":13878772,"url":"https://github.com/kirillplatonov/shopify_graphql","last_synced_at":"2025-04-13T02:19:40.183Z","repository":{"id":37819400,"uuid":"384366246","full_name":"kirillplatonov/shopify_graphql","owner":"kirillplatonov","description":"Less painful way to work with Shopify Graphql API in Ruby.","archived":false,"fork":false,"pushed_at":"2025-03-03T15:11:38.000Z","size":172,"stargazers_count":75,"open_issues_count":2,"forks_count":11,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-04T05:41:53.471Z","etag":null,"topics":["graphql","rails","ruby","shopify"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kirillplatonov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["kirillplatonov"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2021-07-09T08:03:53.000Z","updated_at":"2025-04-02T20:42:41.000Z","dependencies_parsed_at":"2024-01-13T20:39:34.846Z","dependency_job_id":"572a03f1-7ff4-4a1d-ab7f-0203a4c236f6","html_url":"https://github.com/kirillplatonov/shopify_graphql","commit_stats":{"total_commits":116,"total_committers":6,"mean_commits":"19.333333333333332","dds":"0.39655172413793105","last_synced_commit":"3304bbc074861eeae4d8bad6329a23cf7c6a3f19"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirillplatonov%2Fshopify_graphql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirillplatonov%2Fshopify_graphql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirillplatonov%2Fshopify_graphql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirillplatonov%2Fshopify_graphql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kirillplatonov","download_url":"https://codeload.github.com/kirillplatonov/shopify_graphql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248654387,"owners_count":21140289,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphql","rails","ruby","shopify"],"created_at":"2024-08-06T08:01:59.464Z","updated_at":"2025-04-13T02:19:40.163Z","avatar_url":"https://github.com/kirillplatonov.png","language":"Ruby","funding_links":["https://github.com/sponsors/kirillplatonov"],"categories":["Ruby"],"sub_categories":[],"readme":"# Shopify Graphql\n\nLess painful way to work with [Shopify Graphql API](https://shopify.dev/api/admin-graphql) in Ruby. This library is a tiny wrapper on top of [`shopify_api`](https://github.com/Shopify/shopify-api-ruby) gem. It provides a simple API for Graphql calls, better error handling, and Graphql webhooks integration.\n\n## Features\n\n- Simple API for Graphql queries and mutations\n- Conventions for organizing Graphql code\n- ActiveResource-like error handling\n- Graphql and user error handlers\n- Auto-conversion of responses to OpenStruct\n- Graphql webhooks integration for Rails\n- Wrappers for Graphql rate limit extensions\n- Built-in calls for common Graphql calls\n\n## Dependencies\n\n- [`shopify_api`](https://github.com/Shopify/shopify-api-ruby) v10+\n- [`shopify_app`](https://github.com/Shopify/shopify_app) v19+\n\n\u003e For `shopify_api` \u003c v10 use [`0-4-stable`](https://github.com/kirillplatonov/shopify_graphql/tree/0-4-stable) branch.\n\n## Installation\n\nAdd `shopify_graphql` to your Gemfile:\n\n```bash\nbundle add shopify_graphql\n```\n\nThis gem relies on `shopify_app` for authentication so no extra setup is required. But you still need to wrap your Graphql calls with `shop.with_shopify_session`:\n\n```rb\nshop.with_shopify_session do\n  # your calls to graphql\nend\n```\n\n## Conventions\n\nTo better organize your Graphql code use the following conventions:\n\n- Create wrappers for all of your queries and mutations to isolate them\n- Put all Graphql-related code into `app/graphql` folder\n- Use `Fields` suffix to name fields (eg `AppSubscriptionFields`)\n- Use `Get` prefix to name queries (eg `GetProducts` or `GetAppSubscription`)\n- Use imperative to name mutations (eg `CreateUsageSubscription` or `BulkUpdateVariants`)\n\n## Usage examples\n\n### Simple query\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/get_product.rb\n\nclass GetProduct\n  include ShopifyGraphql::Query\n\n  QUERY = \u003c\u003c~GRAPHQL\n    query($id: ID!) {\n      product(id: $id) {\n        handle\n        title\n        description\n      }\n    }\n  GRAPHQL\n\n  def call(id:)\n    response = execute(QUERY, id: id)\n    response.data = response.data.product\n    response\n  end\nend\n```\n\nUsage:\n\n```rb\nproduct = GetProduct.call(id: \"gid://shopify/Product/12345\").data\nputs product.handle\nputs product.title\n```\n\u003c/details\u003e\n\n### Query with custom headers\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nYou can pass custom headers to any GraphQL query or mutation by using the `headers` parameter. A common use case is setting the `Accept-Language` header to retrieve content in specific languages:\n\n```rb\n# Pass custom headers to a direct GraphQL call to get French content\nresponse = ShopifyGraphql.execute(QUERY, headers: { \"Accept-Language\" =\u003e \"fr\" })\n\n# Or create a language-aware query wrapper\nclass GetProduct\n  include ShopifyGraphql::Query\n\n  QUERY = \u003c\u003c~GRAPHQL\n    query($id: ID!) {\n      product(id: $id) {\n        id\n        title\n        description\n        seo {\n          title\n          description\n        }\n      }\n    }\n  GRAPHQL\n\n  def call(id:, language: nil)\n    headers = language ? { \"Accept-Language\" =\u003e language } : nil\n    response = execute(QUERY, headers: headers, id: id)\n    response.data = response.data.product\n    response\n  end\nend\n\n# Then use it to get content in different languages\nfrench_product = GetProduct.call(\n  id: \"gid://shopify/Product/12345\",\n  language: \"fr\"\n).data\n\nputs french_product.title       # =\u003e \"Le Produit\"\nputs french_product.description # =\u003e \"Description en français\"\n\n# Get content in Japanese\njapanese_product = GetProduct.call(\n  id: \"gid://shopify/Product/12345\",\n  language: \"ja\"\n).data\n\nputs japanese_product.title     # =\u003e \"商品名\"\nputs japanese_product.description # =\u003e \"商品の説明\"\n```\n\nThe `Accept-Language` header tells Shopify which language to return the content in. This is particularly useful for:\n- Retrieving translated content for products, collections, and pages\n- Building multi-language storefronts\n- Showing localized SEO content\n\nYou can also use custom headers for other purposes like passing metadata or context with your GraphQL requests.\n\u003c/details\u003e\n\n### Query with data parsing\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/get_product.rb\n\nclass GetProduct\n  include ShopifyGraphql::Query\n\n  QUERY = \u003c\u003c~GRAPHQL\n    query($id: ID!) {\n      product(id: $id) {\n        id\n        title\n        featuredImage {\n          source: url\n        }\n      }\n    }\n  GRAPHQL\n\n  def call(id:)\n    response = execute(QUERY, id: id)\n    response.data = parse_data(response.data.product)\n    response\n  end\n\n  private\n\n  def parse_data(data)\n    OpenStruct.new(\n      id: data.id,\n      title: data.title,\n      featured_image: data.featuredImage\u0026.source\n    )\n  end\nend\n```\n\nUsage:\n\n```rb\nproduct = GetProduct.call(id: \"gid://shopify/Product/12345\").data\nputs product.id\nputs product.title\nputs product.featured_image\n```\n\u003c/details\u003e\n\n### Query with fields\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/product_fields.rb\n\nclass ProductFields\n  FRAGMENT = \u003c\u003c~GRAPHQL\n    fragment ProductFields on Product {\n      id\n      title\n      featuredImage {\n        source: url\n      }\n    }\n  GRAPHQL\n\n  def self.parse(data)\n    OpenStruct.new(\n      id: data.id,\n      title: data.title,\n      featured_image: data.featuredImage\u0026.source\n    )\n  end\nend\n```\n\n```rb\n# app/graphql/get_product.rb\n\nclass GetProduct\n  include ShopifyGraphql::Query\n\n  QUERY = \u003c\u003c~GRAPHQL\n    #{ProductFields::FRAGMENT}\n\n    query($id: ID!) {\n      product(id: $id) {\n        ... ProductFields\n      }\n    }\n  GRAPHQL\n\n  def call(id:)\n    response = execute(QUERY, id: id)\n    response.data = ProductFields.parse(response.data.product)\n    response\n  end\nend\n```\n\nUsage:\n\n```rb\nproduct = GetProduct.call(id: \"gid://shopify/Product/12345\").data\nputs product.id\nputs product.title\nputs product.featured_image\n```\n\u003c/details\u003e\n\n### Simple collection query\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/get_products.rb\n\nclass GetProducts\n  include ShopifyGraphql::Query\n\n  QUERY = \u003c\u003c~GRAPHQL\n    query {\n      products(first: 5) {\n        edges {\n          node {\n            id\n            title\n            featuredImage {\n              source: url\n            }\n          }\n        }\n      }\n    }\n  GRAPHQL\n\n  def call\n    response = execute(QUERY)\n    response.data = parse_data(response.data.products.edges)\n    response\n  end\n\n  private\n\n  def parse_data(data)\n    return [] if data.blank?\n\n    data.compact.map do |edge|\n      OpenStruct.new(\n        id: edge.node.id,\n        title: edge.node.title,\n        featured_image: edge.node.featuredImage\u0026.source\n      )\n    end\n  end\nend\n```\n\nUsage:\n\n```rb\nproducts = GetProducts.call.data\nproducts.each do |product|\n  puts product.id\n  puts product.title\n  puts product.featured_image\nend\n```\n\u003c/details\u003e\n\n### Collection query with fields\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/product_fields.rb\n\nclass ProductFields\n  FRAGMENT = \u003c\u003c~GRAPHQL\n    fragment ProductFields on Product {\n      id\n      title\n      featuredImage {\n        source: url\n      }\n    }\n  GRAPHQL\n\n  def self.parse(data)\n    OpenStruct.new(\n      id: data.id,\n      title: data.title,\n      featured_image: data.featuredImage\u0026.source\n    )\n  end\nend\n```\n\n```rb\n# app/graphql/get_products.rb\n\nclass GetProducts\n  include ShopifyGraphql::Query\n\n  QUERY = \u003c\u003c~GRAPHQL\n    #{ProductFields::FRAGMENT}\n\n    query {\n      products(first: 5) {\n        edges {\n          cursor\n          node {\n            ... ProductFields\n          }\n        }\n      }\n    }\n  GRAPHQL\n\n  def call\n    response = execute(QUERY)\n    response.data = parse_data(response.data.products.edges)\n    response\n  end\n\n  private\n\n  def parse_data(data)\n    return [] if data.blank?\n\n    data.compact.map do |edge|\n      OpenStruct.new(\n        cursor: edge.cursor,\n        node: ProductFields.parse(edge.node)\n      )\n    end\n  end\nend\n```\n\nUsage:\n\n```rb\nproducts = GetProducts.call.data\nproducts.each do |edge|\n  puts edge.cursor\n  puts edge.node.id\n  puts edge.node.title\n  puts edge.node.featured_image\nend\n```\n\u003c/details\u003e\n\n### Collection query with pagination\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/product_fields.rb\n\nclass ProductFields\n  FRAGMENT = \u003c\u003c~GRAPHQL\n    fragment ProductFields on Product {\n      id\n      title\n      featuredImage {\n        source: url\n      }\n    }\n  GRAPHQL\n\n  def self.parse(data)\n    OpenStruct.new(\n      id: data.id,\n      title: data.title,\n      featured_image: data.featuredImage\u0026.source\n    )\n  end\nend\n```\n\n```rb\n# app/graphql/get_products.rb\n\nclass GetProducts\n  include ShopifyGraphql::Query\n\n  LIMIT = 5\n  QUERY = \u003c\u003c~GRAPHQL\n    #{ProductFields::FRAGMENT}\n\n    query($cursor: String) {\n      products(first: #{LIMIT}, after: $cursor) {\n        edges {\n          node {\n            ... ProductFields\n          }\n        }\n        pageInfo {\n          hasNextPage\n          endCursor\n        }\n      }\n    }\n  GRAPHQL\n\n  def call\n    response = execute(QUERY)\n    data = parse_data(response.data.products.edges)\n\n    while response.data.products.pageInfo.hasNextPage\n      response = execute(QUERY, cursor: response.data.products.pageInfo.endCursor)\n      data += parse_data(response.data.products.edges)\n    end\n\n    response.data = data\n    response\n  end\n\n  private\n\n  def parse_data(data)\n    return [] if data.blank?\n\n    data.compact.map do |edge|\n      ProductFields.parse(edge.node)\n    end\n  end\nend\n```\n\nUsage:\n\n```rb\nproducts = GetProducts.call.data\nproducts.each do |product|\n  puts product.id\n  puts product.title\n  puts product.featured_image\nend\n```\n\u003c/details\u003e\n\n### Collection query with block\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/product_fields.rb\n\nclass ProductFields\n  FRAGMENT = \u003c\u003c~GRAPHQL\n    fragment ProductFields on Product {\n      id\n      title\n      featuredImage {\n        source: url\n      }\n    }\n  GRAPHQL\n\n  def self.parse(data)\n    OpenStruct.new(\n      id: data.id,\n      title: data.title,\n      featured_image: data.featuredImage\u0026.source\n    )\n  end\nend\n```\n\n```rb\n# app/graphql/get_products.rb\n\nclass GetProducts\n  include ShopifyGraphql::Query\n\n  LIMIT = 5\n  QUERY = \u003c\u003c~GRAPHQL\n    #{ProductFields::FRAGMENT}\n\n    query($cursor: String) {\n      products(first: #{LIMIT}, after: $cursor) {\n        edges {\n          node {\n            ... ProductFields\n          }\n        }\n        pageInfo {\n          hasNextPage\n          endCursor\n        }\n      }\n    }\n  GRAPHQL\n\n  def call(\u0026block)\n    response = execute(QUERY)\n    response.data.products.edges.each do |edge|\n      block.call ProductFields.parse(edge.node)\n    end\n\n    while response.data.products.pageInfo.hasNextPage\n      response = execute(QUERY, cursor: response.data.products.pageInfo.endCursor)\n      response.data.products.edges.each do |edge|\n        block.call ProductFields.parse(edge.node)\n      end\n    end\n\n    response\n  end\nend\n```\n\nUsage:\n\n```rb\nGetProducts.call do |product|\n  puts product.id\n  puts product.title\n  puts product.featured_image\nend\n```\n\u003c/details\u003e\n\n### Collection query with nested pagination\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\nDefinition:\n\n```rb\n# app/graphql/get_collections_with_products.rb\n\nclass GetCollectionsWithProducts\n  include ShopifyGraphql::Query\n\n  COLLECTIONS_LIMIT = 1\n  PRODUCTS_LIMIT = 25\n  QUERY = \u003c\u003c~GRAPHQL\n    query ($cursor: String) {\n      collections(first: #{COLLECTIONS_LIMIT}, after: $cursor) {\n        edges {\n          node {\n            id\n            title\n            products(first: #{PRODUCTS_LIMIT}) {\n              edges {\n                node {\n                  id\n                }\n              }\n            }\n          }\n        }\n        pageInfo {\n          hasNextPage\n          endCursor\n        }\n      }\n    }\n  GRAPHQL\n\n  def call\n    response = execute(QUERY)\n    data = parse_data(response.data.collections.edges)\n\n    while response.data.collections.pageInfo.hasNextPage\n      response = execute(QUERY, cursor: response.data.collections.pageInfo.endCursor)\n      data += parse_data(response.data.collections.edges)\n    end\n\n    response.data = data\n    response\n  end\n\n  private\n\n  def parse_data(data)\n    return [] if data.blank?\n\n    data.compact.map do |edge|\n      OpenStruct.new(\n        id: edge.node.id,\n        title: edge.node.title,\n        products: edge.node.products.edges.map do |product_edge|\n          OpenStruct.new(id: product_edge.node.id)\n        end\n      )\n    end\n  end\nend\n```\n\nUsage:\n\n```rb\ncollections = GetCollectionsWithProducts.call.data\ncollections.each do |collection|\n  puts collection.id\n  puts collection.title\n  collection.products.each do |product|\n    puts product.id\n  end\nend\n```\n\u003c/details\u003e\n\n### Mutation\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\n\nDefinition:\n\n```rb\n# app/graphql/update_product.rb\n\nclass UpdateProduct\n  include ShopifyGraphql::Mutation\n\n  MUTATION = \u003c\u003c~GRAPHQL\n    mutation($input: ProductInput!) {\n      productUpdate(input: $input) {\n        product {\n          id\n          title\n        }\n        userErrors {\n          field\n          message\n        }\n      }\n    }\n  GRAPHQL\n\n  def call(input:)\n    response = execute(MUTATION, input: input)\n    response.data = response.data.productUpdate\n    handle_user_errors(response.data)\n    response\n  end\nend\n```\n\nUsage:\n\n```rb\nresponse = UpdateProduct.call(input: { id: \"gid://shopify/Product/123\", title: \"New title\" })\nputs response.data.product.title\n```\n\u003c/details\u003e\n\n### Graphql call without wrapper\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\n\n```rb\nPRODUCT_UPDATE_MUTATION = \u003c\u003c~GRAPHQL\n  mutation($input: ProductInput!) {\n    productUpdate(input: $input) {\n      product {\n        id\n        title\n      }\n      userErrors {\n        field\n        message\n      }\n    }\n  }\nGRAPHQL\n\nresponse = ShopifyGraphql.execute(\n  PRODUCT_UPDATE_MUTATION,\n  input: { id: \"gid://shopify/Product/12345\", title: \"New title\" }\n)\nresponse = response.data.productUpdate\nShopifyGraphql.handle_user_errors(response)\n```\n\u003c/details\u003e\n\n## Built-in Graphql calls\n\n- `ShopifyGraphql::CurrentShop`:\n\n  Equivalent to `ShopifyAPI::Shop.current`. Usage example:\n\n  ```rb\n  shop = ShopifyGraphql::CurrentShop.call\n  puts shop.name\n  ```\n\n  Or with locales (requires `read_locales` scope):\n\n  ```rb\n  shop = ShopifyGraphql::CurrentShop.call(with_locales: true)\n  puts shop.primary_locale\n  puts shop.shop_locales\n  ```\n\n- `ShopifyGraphql::CancelSubscription`\n- `ShopifyGraphql::CreateRecurringSubscription`\n- `ShopifyGraphql::CreateUsageSubscription`\n- `ShopifyGraphql::GetAppSubscription`\n- `ShopifyGraphql::UpsertPrivateMetafield`\n- `ShopifyGraphql::DeletePrivateMetafield`\n- `ShopifyGraphql::CreateBulkMutation`\n- `ShopifyGraphql::CreateBulkQuery`\n- `ShopifyGraphql::CreateStagedUploads`\n- `ShopifyGraphql::GetBulkOperation`\n\nBuilt-in wrappers are located in [`app/graphql/shopify_graphql`](/app/graphql/shopify_graphql/) folder. You can use them directly in your apps or as an example to create your own wrappers.\n\n## Rate limits\n\nThe gem exposes Graphql rate limit extensions in response object:\n\n- `points_left`\n- `points_limit`\n- `points_restore_rate`\n- `query_cost`\n\nAnd adds a helper to check if available points lower than threshold (useful for implementing API backoff):\n\n- `points_maxed?(threshold: 100)`\n\nUsage example:\n\n```rb\nresponse = GetProduct.call(id: \"gid://shopify/Product/PRODUCT_GID\")\nresponse.points_left # =\u003e 1999\nresponse.points_limit # =\u003e 2000.0\nresponse.points_restore_rate # =\u003e 100.0\nresponse.query_cost # =\u003e 1\nresponse.points_maxed?(threshold: 100) # =\u003e false\n```\n\n## Custom apps\n\nIn custom apps, if you're using `shopify_app` gem, then the setup is similar public apps. Except `Shop` model which must include class method to make queries to your store:\n\n```rb\n# app/models/shop.rb\nclass Shop \u003c ActiveRecord::Base\n  include ShopifyApp::ShopSessionStorageWithScopes\n\n  def self.system\n    new(\n      shopify_domain: \"MYSHOPIFY_DOMAIN\",\n      shopify_token: \"API_ACCESS_TOKEN_FOR_CUSTOM_APP\"\n    )\n  end\nend\n```\n\nUsing this method, you should be able to make API calls like this:\n\n```rb\nShop.system.with_shopify_session do\n  GetOrder.call(id: order.shopify_gid)\nend\n```\n\nIf you're not using `shopify_app` gem, then you need to setup `ShopifyAPI::Context` manually:\n\n```rb\n# config/initializers/shopify_api.rb\nShopifyAPI::Context.setup(\n  api_key: \"XXX\",\n  api_secret_key: \"XXXX\",\n  scope: \"read_orders,read_products\",\n  is_embedded: false,\n  api_version: \"2024-07\",\n  is_private: true,\n)\n```\n\nAnd create another method in Shop model to make queries to your store:\n\n```rb\n# app/models/shop.rb\ndef Shop\n  def self.with_shopify_session(\u0026block)\n    ShopifyAPI::Auth::Session.temp(\n      shop: \"MYSHOPIFY_DOMAIN\",\n      access_token: \"API_ACCESS_TOKEN_FOR_CUSTOM_APP\",\n      \u0026block\n    )\n  end\nend\n```\n\nUsing this method, you should be able to make API calls like this:\n\n```rb\nShop.with_shopify_session do\n  GetOrder.call(id: order.shopify_gid)\nend\n```\n\n## Graphql webhooks (deprecated)\n\n\u003e [!WARNING]\n\u003e ShopifyGraphql webhooks are deprecated and will be removed in v3.0. Please use `shopify_app` gem for handling webhooks. See [`shopify_app` documentation](https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/webhooks.md) for more details.\n\nThe gem has built-in support for Graphql webhooks (similar to `shopify_app`). To enable it add the following config to `config/initializers/shopify_app.rb`:\n\n```rb\nShopifyGraphql.configure do |config|\n  # Webhooks\n  webhooks_prefix = \"https://#{Rails.configuration.app_host}/graphql_webhooks\"\n  config.webhook_jobs_namespace = 'shopify/webhooks'\n  config.webhook_enabled_environments = ['development', 'staging', 'production']\n  config.webhooks = [\n    { topic: 'SHOP_UPDATE', address: \"#{webhooks_prefix}/shop_update\" },\n    { topic: 'APP_SUBSCRIPTIONS_UPDATE', address: \"#{webhooks_prefix}/app_subscriptions_update\" },\n    { topic: 'APP_UNINSTALLED', address: \"#{webhooks_prefix}/app_uninstalled\" },\n  ]\nend\n```\n\nAnd add the following routes to `config/routes.rb`:\n\n```rb\nmount ShopifyGraphql::Engine, at: '/'\n```\n\nTo register defined webhooks you need to call `ShopifyGraphql::UpdateWebhooksJob`. You can call it manually or use `AfterAuthenticateJob` from `shopify_app`:\n\n```rb\n# config/initializers/shopify_app.rb\nShopifyApp.configure do |config|\n  # ...\n  config.after_authenticate_job = {job: \"AfterAuthenticateJob\", inline: true}\nend\n```\n\n```rb\n# app/jobs/after_install_job.rb\nclass AfterInstallJob \u003c ApplicationJob\n  def perform(shop)\n    # ...\n    update_webhooks(shop)\n  end\n\n  def update_webhooks(shop)\n    ShopifyGraphql::UpdateWebhooksJob.perform_later(\n      shop_domain: shop.shopify_domain,\n      shop_token: shop.shopify_token\n    )\n  end\nend\n```\n\nTo handle webhooks create jobs in `app/jobs/webhooks` folder. The gem will automatically call them when new webhooks are received. The job name should match the webhook topic name. For example, to handle `APP_UNINSTALLED` webhook create `app/jobs/webhooks/app_uninstalled_job.rb`:\n\n```rb\nclass Webhooks::AppUninstalledJob \u003c ApplicationJob\n  queue_as :default\n\n  def perform(shop_domain:, webhook:)\n    shop = Shop.find_by!(shopify_domain: shop_domain)\n    # handle shop uninstall\n  end\nend\n```\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkirillplatonov%2Fshopify_graphql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkirillplatonov%2Fshopify_graphql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkirillplatonov%2Fshopify_graphql/lists"}