Obtaining an inventory total for variant using GraphQL

alibby
New Member
9 0 0

Hi - I'm new to Shopify: love what I see, but am struggling with what I hope will be one small problem:

I'm trying to get the inventory count for a variant - I've scoured the Shopify dev site and docs, but haven't found anything that works for me. So far, I have managed to get back a JSON object with details of a test variant, using the steps at this link. This gives me results when I use https://havecakeeatcake.myshopify.com/admin/api/2020-07/products/5557596127382/variants/356239605433....

I'd really like to get the same data using GraphQL if at all possible - I can see there is an option to do this on that page, but if I use the GraphQL example given:

query getVariantByID($id: ID!) {
  productVariant(id: $id) {
    id
    title
    inventoryItem {
      id
    }
  }
}

I've updated the productVariant entry to shopifyProductVariant (I hope that's right!), but the inventoryItem entry does not feature at all in my GraphiQL explorer. How would I turn the REST query into something I can use in GraphQL? I'm using Gatsby / GraphQL as the basis for my site, so would like to return something that I can then consume in my site.

I suspect there is something not set correctly somewhere, but am not sure where - I've checked my Admin API permissions, and have Inventory and Products both set to read-only; is this sufficient? At this stage I'm not looking to update this value; it's purely a POC to confirm I can return details from the Shopify Admin API into my site when needed. The site is also a single location.

Any ideas? I'd love to see this working on my site - please help!

Replies 16 (16)

Kevin_A
Shopify Staff
318 42 61

Hey @alibby 

You are looking for the inventoryLevels connection: 

{
  productVariant(id:"gid://shopify/ProductVariant/<id>"){
    inventoryItem{
      id
      inventoryLevels(first:5){
        edges{
          node{
            available
          }
        }
      }
    }
  }
}

 

Kevin_A | Solutions Engineer @ Shopify 
 - Was my reply helpful? Click Like to let me know! 
 - Was your question answered? Mark it as an Accepted Solution
 - To learn more visit Shopify.dev or the Shopify Web Design and Development Blog

alibby
New Member
9 0 0

Hey Kevin_A,

Thanks for the info - unfortunately though it's not worked for me!

I dropped it in to GraphiQL, and adjusted the id value being passed with a known ID from one of my variants - it wasn't recognising the value I had set as a query variable initially, so thought hardcoding it would at least confirm if it worked:

{
  productVariant(id:"gid://shopify/ProductVariant/35623960543382"){
    inventoryItem{
      id
      inventoryLevels(first:5){
        edges{
          node{
            available
          }
        }
      }
    }
  }
}

When I ran the query, it initially came back with "Cannot query field productVariant on type Query. Did you mean shopifyProductVariant, or allShopifyProductVariant?"

I changed it to the former, at which point it threw an error on the id value being passed in as the ID - it came back with an "Expected type StringQueryOperatorInput, found..." error, and it also complained with a second error, which was "Cannot query field inventoryItem on type Query." 

As a quick check, I also tried allShopifyProductVariant too - same issue with inventoryItem appeared, but this time it complained of not recognising the id being passed to the query.

I'm a bit stuck as to where to go next - is it possible that I've missed something in my config somewhere, which means not all of the relevant fields are displayed in GraphiQL?

Gregarican
Shopify Partner
1033 86 285

Strange, as I just ran the query below in the GraphiQL app installed on my test store. It came back with results just fine?

{
  productVariant(id: "gid://shopify/ProductVariant/31517938876468") {
    inventoryItem {
      id
      inventoryLevels(first: 5) {
        edges {
          node {
            location {
              id
              name
            }
            available
          }
        }
      }
    }
  }
}

 

{
  "data": {
    "productVariant": {
      "inventoryItem": {
        "id": "gid://shopify/InventoryItem/33060349739060",
        "inventoryLevels": {
          "edges": [
            {
              "node": {
                "location": {
                  "id": "gid://shopify/Location/203128",
                  "name": "Sawmill"
                },
                "available": 2
              }
            },
            {
              "node": {
                "location": {
                  "id": "gid://shopify/Location/17431658558",
                  "name": "Easton"
                },
                "available": 0
              }
            },
            {
              "node": {
                "location": {
                  "id": "gid://shopify/Location/17431691326",
                  "name": "Weber"
                },
                "available": 0
              }
            }
          ]
        }
      }
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 14,
      "actualQueryCost": 10,
      "throttleStatus": {
        "maximumAvailable": 1000,
        "currentlyAvailable": 990,
        "restoreRate": 50
      }
    }
  }
}

 

alibby
New Member
9 0 0

Mmm - that is indeed strange! Thanks though for checking.

I'm running my query directly from http://localhost:8000/__graphql - when you say "GraphQL app", do you mean this route, or something else? I'm keen to see if I can display the value on my individual product pages, hence why I'm doing it via this URL.

I'm beginning to think there may be an issue with how my Shopify access is set up - in my Gatsby config file, I've got this:

    {
      resolve: `gatsby-source-shopify`,
      options: {
        shopName: process.env.SHOP_NAME,
        accessToken: process.env.SHOPIFY_ACCESS_TOKEN,
        verbose: true,
        includeCollections: ["shop", "product", "inventory"],
      },
    },

I've included the "inventory" collection in the above code, on the basis that I believe this is necessary to view Inventory items - is this correct? I've also checked the API permissions, and all are set to read-only.

EDIT: I've also tried in the Shopify GraphQL app at https://shopify.dev/graphiql/admin-graphiql, using your code, and a variety of different IDs (including both product and variants - just in case!). This is what I'm getting back:

{
  "data": {
    "productVariant": null
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 14,
      "actualQueryCost": 1,
      "throttleStatus": {
        "maximumAvailable": 1000,
        "currentlyAvailable": 999,
         "restoreRate": 50
      }
    }
  }
}

Have I missed anything?

Gregarican
Shopify Partner
1033 86 285

That's what I was using --- the GraphiQL app installed on my test shop --> https://shopify.dev/tools/graphiql-admin-api. If you are coming back with no results with that then I'd suggest verifying you are working with a legitimate variant ID. Issue a simple query in the GraphiQL app like this:

{
  productVariant(id: "gid://shopify/ProductVariant/31513252823092") {
    id
    displayName
  }
}

If that's coming back null as well, then that's an invalid variant ID... 

alibby
New Member
9 0 0

Hey Kevin_A,

I think we're making progress...kind of!

I've installed the GraphQL app; by trial and error, I worked out that part of the problem seems to be an invalid variant ID! If I run up your "ID check" code, I get this:

{
  "data": {
    "productVariant": {
      "id": "gid://shopify/ProductVariant/35623960543382",
      "displayName": "Chocolate & hazelnut cupcake - Individual cakes"
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 1,
      "actualQueryCost": 1,
      "throttleStatus": {
        "maximumAvailable": 1000,
        "currentlyAvailable": 999,
        "restoreRate": 50
      }
    }
  }
}

However, if I retrofit that ID into your first query, I get this error returned:

{
  "data": {
    "productVariant": null
  },
  "errors": [
    {
      "message": "Access denied for inventoryItem field. Required access: `read_inventory` access scope.",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "productVariant",
        "inventoryItem"
      ],
      "extensions": {
        "code": "ACCESS_DENIED",
        "documentation": "https://shopify.dev/docs/admin-api/access-scopes",
        "requiredAccessScope": "read_inventory"
      }
    }
  ],

I'm not sure where to set these permissions - I've checked the Storefront API permissions for my app and they all show as being enabled. Any ideas please? I'm assuming that if I can get this working, I can then plug this GraphQL code into my Gatsby/React code and it should produce the same result, right?

Gregarican
Shopify Partner
1033 86 285

If you think your API permissions indeed cover all relevant scopes, then you can verify this with your access token. Trying hitting this API endpoint and it should return the scopes you have access to --> https://shopify.dev/docs/admin-api/rest/reference/access/accessscope. If read_inventory isn't listed in the response then that's definitely the issue. 

alibby
New Member
9 0 0

Hi - I've checked the Storefront API; this had Inventory and Products enabled, and that I believe the former covers the read_inventory access scope, right?

I tried the check URL you've suggested, which came back with this - confirming that it does indeed appear to be a permissions issue:

HTTP/1.1 200 OK
{
  "access_scopes": [
    {
      "handle": "read_products"
    },
    {
      "handle": "write_orders"
    },
    {
      "handle": "read_orders"
    }
  ]
}

However the read_inventory access scope was already set to read access, but it seems this permission hasn't filtered down to the GraphQL app, as this is still showing access denied errors. I've tried changing this to read and write to see if both levels are needed; that has had no effect. 

Is it possible that I need to enable other access scopes, or is just the Inventory one sufficient?

Gregarican
Shopify Partner
1033 86 285

You might need to re-auth the app again maybe so the proper scopes are included in the URL parameters? 

 

https://shopify.dev/tutorials/authenticate-with-oauth

 

alibby
New Member
9 0 0

Thanks for your ongoing support - it is appreciated!

I've had a look at the URL you've suggested, and put together what I think is the right URL based on the values given in the reauthorize OAuth permissions section. I ended up with this:

http://myshopify.com/admin/oauth/authorize?client_id=<MY API KEY>&scope=read_inventory,write_invento...  (I've replaced the key used with text here, to keep it secure!)

This brought up a notification which suggested I needed to go to my shop's domains, and hit Connect to existing domain, then enter myshopify.com - I tried this, but the dashboard complained that it wouldn't accept any domain with Shopify in it, and that my Shopify domain already exists (which it does). I've tried swapping out the API key for the Storefront access token too and included the redirect to point back to my Shopify store - this showed the same issue.

Have I done this right? It feels like I'm going down a rabbit warren (or wrong path - if you pardon the analogy!) I've also tried adding in my domain at the beginning of the URL above, but this threw a invalid_request error: "The Shopify API application does not support oauth". I feel like I'm doing something wrong, and a little bit lost - any chance you could please help me get back on the right track again? 

Gregarican
Shopify Partner
1033 86 285

What do you have defined as the redirect_uri in this oauth request? That might be the issue. If you don't have one defined then the GET response will just pop back to the shopify page, which isn't correct. You need to define a redirect_uri. 

Here below are the specifics, taken from the link I provided you. Besides that the first part of the full URL should be https://{my_shop}.myshopify.com where the {my_shop} represents the shop where the app is going to be installed.

 

{redirect_uri}: The URL to which a user is redirected after authorizing the client. The complete URL specified here must be added to your app as an allowed redirection URL, as defined in the Partner Dashboard.

 

alibby
New Member
9 0 0

Unfortunately it's not helped, I'm afraid - I tried adding in &redirect-uri=localhost as the domain to my original OAuth URL, as my shop is being run from localhost (as a Gatsby dev application). This threw an error on the "connect domain" screen from before - this time I got "you must enter a full domain and extension". I tried also with havecakeeatcake.myshopify.com, just in case it needed this; it threw the same error as on my previous post...

 

abohannon
Shopify Partner
2 0 0

`gatsby-source-shopify` - for whatever reason - doesn't give you access to all the GraphQL queries available via the Shopify API, `totalInventory` and `quantityAvailable` being two of these fields. I created an issue to add support for these fields specifically: https://github.com/gatsbyjs/gatsby/issues/26321. As I write this it's been 12 days with no discussion, so I don't expect they will be added any time soon. One work around is you could query the Shopify API directly via REST and not use the gatsby plugin.

alibby
New Member
9 0 0

Thank you @abohannon for your help on this - it would be good to have it available via GraphQL if at all possible.

I have had a look at it using REST - I managed to get something working which makes the content available via GraphQL using the gatsby-transformer-json plugin. It's not perfect, as it would require adding a JSON file for updates, but as what I'm doing is something of a POC, it works for now! If you do get any result with the issue report you've filed, I'd be happy to help test if needed 😉

Gregarican
Shopify Partner
1033 86 285

It were me, if this Gatsby package is open source I would just download the source, update it to include the missing properties and whatnot, and deploy this updated version for what I need. I've had to do this with similar projects in the past and generally found it easier than working around things other ways. Usually just looking at how the properties are defined for other exposed elements gives a clue how to add new ones. 

mRichman
Visitor
2 0 0

Hi,

I'm trying to do something similar, but get a different field. However I'm facing the error: "Field 'productVariants' doesn't accept argument 'id'". The same call works fine for products if I switch out the top line to "product(id: "gid://shopify/Product/6728415838363") {"

My graphQL:

 

{
  productVariants(id:"gid://shopify/ProductVariant/39865779749019") {
    id
    storefrontId
}
}

 

 

full error:

 

{
    "errors": [
        {
            "message": "Field 'productVariants' doesn't accept argument 'id'",
            "locations": [
                {
                    "line": 2,
                    "column": 19
                }
            ],
            "path": [
                "query",
                "productVariants",
                "id"
            ],
            "extensions": {
                "code": "argumentNotAccepted",
                "name": "productVariants",
                "typeName": "Field",
                "argumentName": "id"
            }
        },
        {
            "message": "Field 'id' doesn't exist on type 'ProductVariantConnection'",
            "locations": [
                {
                    "line": 3,
                    "column": 5
                }
            ],
            "path": [
                "query",
                "productVariants",
                "id"
            ],
            "extensions": {
                "code": "undefinedField",
                "typeName": "ProductVariantConnection",
                "fieldName": "id"
            }
        },
        {
            "message": "Field 'storefrontId' doesn't exist on type 'ProductVariantConnection'",
            "locations": [
                {
                    "line": 4,
                    "column": 5
                }
            ],
            "path": [
                "query",
                "productVariants",
                "storefrontId"
            ],
            "extensions": {
                "code": "undefinedField",
                "typeName": "ProductVariantConnection",
                "fieldName": "storefrontId"
            }
        }
    ]
}

 

 
If you have any ideas I'd be incredibly grateful. I'm using the 2021-04 api.

Thanks.