gatsby-source-graphql and GraphQL Shopify Admin API

Created on 12 May 2019  Â·  9Comments  Â·  Source: gatsbyjs/gatsby

The repo to reproduce the code can be found HERE

I’m working on sourcing and combining data from Shopify Storefront and GraphQL Admin APIs into Gatsby. For sourcing Storefront API I use gatsby-source-shopify, though I wasn’t able to find source plugin for _Shopify Admin API_ and ended up using gatsby-source-graphql.

I have created a private Shopify app and granted all permissions and was able to fetch data using curl example from Shopify GraphQL Admin API documentation.

curl -X POST \
"https://shop-name.myshopify.com/admin/api/2019-04/graphql.json" \
-H "Content-Type: application/graphql" \
-H "X-Shopify-Access-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d '
{
  shop {
    name
  }
}
'

Output:

{"data":{"shop":{"name":"Shop Name"}},"extensions":{"cost":{"requestedQueryCost":1,"actualQueryCost":1,"throttleStatus":{"maximumAvailable":1000.0,"currentlyAvailable":999,"restoreRate":50.0}}}}

Error message:

success open and validate gatsby-configs — 0.007 s
success load plugins — 0.345 s
success onPreInit — 0.005 s
success initialize cache — 0.015 s
success copy gatsby files — 0.068 s
success onPreBootstrap — 0.006 s
error Plugin gatsby-source-graphql returned an error


  Error: Parse error on "operationName" (STRING) at [1, 2]


gatsby-source-shopify/sandbox starting to fetch data from Shopify

gatsby-source-shopify/sandbox fetched and processed productTypes: 287.590ms

gatsby-source-shopify/sandbox fetched and processed policies: 297.440ms

gatsby-source-shopify/sandbox fetched and processed blogs: 302.418ms

gatsby-source-shopify/sandbox fetched and processed articles: 344.917ms

gatsby-source-shopify/sandbox fetched and processed collections: 8603.841ms


gatsby-source-shopify/sandbox fetched and processed products: 20362.561ms

gatsby-source-shopify/sandbox finished fetching data from Shopify: 20369.525ms
warning The gatsby-source-graphql plugin has generated no Gatsby nodes. Do you need it?
success source and transform nodes — 21.169 s
warning Multiple node fields resolve to the same GraphQL field `ShopifyArticle.blog` - [`blog`, `blog___NODE`]. Gatsby will use `blog___NODE`.
warning Multiple node fields resolve to the same GraphQL field `ShopifyCollection.products` - [`products`, `products___NODE`]. Gatsby will use `products___NODE`.
warning Multiple node fields resolve to the same GraphQL field `ShopifyProduct.options` - [`options`, `options___NODE`]. Gatsby will use `options___NODE`.
warning Multiple node fields resolve to the same GraphQL field `ShopifyProduct.variants` - [`variants`, `variants___NODE`]. Gatsby will use `variants___NODE`.
success building schema — 0.283 s
success createPages — 0.000 s
success createPagesStatefully — 0.042 s
success onPreExtractQueries — 0.005 s
success update schema — 0.019 s
success extract queries from components — 0.098 s
success run static queries — 0.021 s — 2/2 96.96 queries/second
success run page queries — 0.014 s — 4/4 301.32 queries/second
success write out page data — 0.012 s
success write out redirect data — 0.001 s
success Build manifest and related icons — 0.104 s
success onPostBootstrap — 0.106 s

info bootstrap finished - 23.946 s

 WARNING  Compiled with 1 warnings                                                         6:04:43 PM

Module Warning (from ./node_modules/eslint-loader/index.js):

/Users/skok/dev/gatsby-shopify-starter/src/components/collections.js
  2:10  warning  'useStaticQuery' is defined but never used  no-unused-vars
  2:26  warning  'graphql' is defined but never used         no-unused-vars

✖ 2 problems (0 errors, 2 warnings)


You may use special comments to disable some warnings.
Use // eslint-disable-next-line to ignore the next line.
Use /* eslint-disable */ to ignore all warnings in a file.

You can now view gatsby-shopify in the browser.

  http://localhost:8000/

View GraphiQL, an in-browser IDE, to explore your site's data and schema

  http://localhost:8000/___graphql

Note that the development build is not optimized.
To create a production build, use npm run build

warning âš  ï½¢wdmï½£:
info ℹ 「wdm」: Compiled with warnings.
 WAIT  Compiling...                                                                        6:04:43 PM

info ℹ 「wdm」: Compiling...
 WARNING  Compiled with 1 warnings                                                         6:04:43 PM

Module Warning (from ./node_modules/eslint-loader/index.js):

/Users/skok/dev/gatsby-shopify-starter/src/components/collections.js
  2:10  warning  'useStaticQuery' is defined but never used  no-unused-vars
  2:26  warning  'graphql' is defined but never used         no-unused-vars

✖ 2 problems (0 errors, 2 warnings)


You may use special comments to disable some warnings.
Use // eslint-disable-next-line to ignore the next line.
Use /* eslint-disable */ to ignore all warnings in a file.
warning âš  ï½¢wdmï½£:
info ℹ 「wdm」: Compiled with warnings.

gatsby-config.js:

require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`
});

module.exports = {
  siteMetadata: {
    title: `Gatsby Shopify`,
    description: `Gatsby Shopify Starter.`,
    author: `@iamskok`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: "gatsby-source-graphql",
      options: {
        // This type will contain remote schema Query type
        typeName: "ShopifyAdmin",
        // This is field under which it's accessible
        fieldName: "admin",
        // Url to query from
        url: `https://${process.env.SHOP_NAME}.myshopify.com/admin/api/2019-04/graphql.json`,
        headers: {
          // Learn about environment variables: https://gatsby.dev/env-vars
          'X-Shopify-Access-Token': `${process.env.ADMIN_PASSWORD}`,
          'Content-Type': 'application/graphql'
        },
        fetchOptions: {
          method: 'POST'
        },
        refetchInterval: 60
      },
    },
    {
      resolve: `gatsby-source-shopify`,
      options: {
        // The domain name of your Shopify shop. This is required.
        // Example: 'gatsby-source-shopify-test-shop' if your Shopify address is
        // 'gatsby-source-shopify-test-shop.myshopify.com'.
        shopName: process.env.SHOP_NAME,

        // An API access token to your Shopify shop. This is required.
        // You can generate an access token in the "Manage private apps" section
        // of your shop's Apps settings. In the Storefront API section, be sure
        // to select "Allow this app to access your storefront data using the
        // Storefront API".
        // See: https://help.shopify.com/api/custom-storefronts/storefront-api/getting-started#authentication
        accessToken: process.env.STOREFRONT_ACCESS_TOKEN,

        // Set verbose to true to display a verbose output on `npm run develop`
        // or `npm run build`. This prints which nodes are being fetched and how
        // much time was required to fetch and process the data.
        // Defaults to true.
        verbose: true,
      },
    },
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
  ],
}

./src/collections.js:

import React from 'react'
import { useStaticQuery, graphql } from 'gatsby'

const Collections = () => {
  const storefrontData = useStaticQuery(
    graphql`
      query {
        allShopifyCollection {
          edges {
            node {
              id
              description
              descriptionHtml
              handle
              image {
                altText
                id
                src
                localFile {
                  childImageSharp {
                    fluid(maxWidth: 910) {
                      ...GatsbyImageSharpFluid_withWebp_tracedSVG
                    }
                  }
                }
              }
              products {
                id
                handle
                title
              }
              title
              updatedAt
            }
          }
        }
      }
    `
  )

  const adminData = useStaticQuery(
    graphql`
      query {
        admin {
          shop {
            name
          }
        }
      }
    `
  )

  console.log('storefront data:', JSON.stringify(storefrontData));
  console.log('admin data:', JSON.stringify(adminData));

  return (
    <div className="collections">
      <h2>Collections</h2>
    </div>
  )
}

export default Collections
````

`index.js`:

```js
import React from 'react'
import Layout from '../components/layout'
import Collections from '../components/collections'

const IndexPage = () => (
  <Layout>
    <h1>Shopify</h1>
    <Collections />
  </Layout>
)

export default IndexPage

package.json:

{
  "name": "gatsby-shopify",
  "private": true,
  "description": "Gatsby Shopify Starter",
  "version": "0.1.0",
  "author": "Vladimir Skok <[email protected]>",
  "dependencies": {
    "gatsby": "^2.4.2",
    "gatsby-image": "^2.0.41",
    "gatsby-plugin-manifest": "^2.1.1",
    "gatsby-plugin-offline": "^2.1.0",
    "gatsby-plugin-react-helmet": "^3.0.12",
    "gatsby-plugin-sharp": "^2.0.36",
    "gatsby-source-filesystem": "^2.0.33",
    "gatsby-source-graphql": "^2.0.18",
    "gatsby-source-shopify": "^2.0.28",
    "gatsby-transformer-sharp": "^2.1.19",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-helmet": "^5.2.1"
  },
  "devDependencies": {
    "prettier": "^1.17.0"
  },
  "keywords": [
    "gatsby"
  ],
  "license": "MIT",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "format": "prettier --write src/**/*.{js,jsx}",
    "start": "npm run develop",
    "serve": "gatsby serve",
    "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\""
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/iamskok/gatsby-shopify-starter"
  },
  "bugs": {
    "url": "https://github.com/iamskok/gatsby-shopify-starter/issues"
  }
}

The expected result is to make a successful GraphQL query to Shopify Admin API. The actual result is server error with 400 status code.

stale? bug question or discussion

Most helpful comment

Ah, thanks for the clarification. I need inventory information which is only available on the admin API. Any chance you could re-open this issue as it is about a bug that isn't fixed that people might still need to be fixed?

Thanks!

All 9 comments

Working example of GraphQL Shopify Admin API query can be found in /plugins of this repo. More
details on retrieving metafields from Shopify GraphQL Storefront API can be found in #14193

@iamskok could you elaborate on the solution? I'm having the exact same problem.

The repo you linked to has the source to graphql-source-shopify, it isn't an example of how to query the shopify admin api using graphql-source-graphql? Or is it?

@altano My purpose was to query everything from Storefront + metafields so, in the beginning, I thought that I will have to fetch data from Storefront + Admin APIs and combine it together, but it happened so @Shopify recently released the feature which lets you retrieve metafields on top of everything else Storefront API provides.

Couple of things to note:

  • At the moment this feature works only with product and productVariant metafields. I hope @Shopify will make other types of metafields available shortly. (It has to be on their roadmap , because it's already in their error messages :neckbeard: )
  • You'll still have to whitelist metafields using Admin API.

Shameless plug. gatsby-plugin-shopify-metafields has to make a process of whitelisting metafields much shorter.

Ah, thanks for the clarification. I need inventory information which is only available on the admin API. Any chance you could re-open this issue as it is about a bug that isn't fixed that people might still need to be fixed?

Thanks!

I'm also facing this issue

You can also set the metafields visibility by adding the shopify graphql app to your store and then query

mutation metafieldStorefrontVisibilityCreate {
  metafieldStorefrontVisibilityCreate(input: {namespace: custom_fields, key: isbn, ownerType: PRODUCT}) {
    metafieldStorefrontVisibility {
      id
    }
    userErrors {
      field
      message
    }
  }
}

Then the following query will work using the Storefront api

{
  shop {
    products(first: 5) {
      edges {
        node {
          metafields(namespace: "custom_fields", first: 10) {
            edges {
              node {
                key
                value
              }
            }
          }
          id
          handle
        }
      }
    }
  }
}

However querying the following using gatsby-source-shopify doesn't work as metafields isn't a known field

allShopifyProduct {
  edges {
    node {
      metafields(namespace: "custom_fields", first: 10) {
        edges {
          node {
            key
            value
          }
        }
      }
      handle
    }
}

@jockjocko I’m actually working on a PR for Gatsby-source-shopify which will allow to
query metafields, it’s in a private repo at the moment(still have some
clean up to do). I think it might speed up the process if you’ll open a
feature request issue and assign me to it. Thank you

On Mon, Jul 1, 2019 at 16:02 Jack Davies notifications@github.com wrote:

I'm also facing this issue

You can also set the metafields visibility by adding the shopify
graphql app https://shopify-graphiql-app.shopifycloud.com/to your store
and then query

mutation metafieldStorefrontVisibilityCreate {
metafieldStorefrontVisibilityCreate(input: {namespace: custom_fields, key: isbn, ownerType: PRODUCT}) {
metafieldStorefrontVisibility {
id
}
userErrors {
field
message
}
}
}

Then the following query will work using the Storefront api

{
shop {
products(first: 5) {
edges {
node {
metafields(namespace: "custom_fields", first: 10) {
edges {
node {
key
value
}
}
}
id
handle
}
}
}
}
}

However querying the following using gatsby-source-shopify doesn't work as
metafields isn't a known field

allShopifyProduct {
edges {
node {
metafields(namespace: "custom_fields", first: 10) {
edges {
node {
key
value
}
}
}
handle
}
}

—
You are receiving this because you modified the open/close state.

Reply to this email directly, view it on GitHub
https://github.com/gatsbyjs/gatsby/issues/13981?email_source=notifications&email_token=ABHIGDZVCFXEK4ZCXJRDQPDP5KEHPA5CNFSM4HMJQRO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODY7SQOQ#issuecomment-507455546,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABHIGD7DEOT7AX6DCEKNBMDP5KEHPANCNFSM4HMJQROQ
.

I dug into this problem today. The first issue is straightforward:

We were making the query with the following header: Content-Type: application/graphql. This was getting added to content-type: application/json (note the different capitalization) but the graphql was second. The Shopify servers don't like this MIME type and barf out the above error. The Shopify server expects the sent query to be JSON. Removing the content-type specification from gatsby-config.js gets us one step farther. Then we hit another error:

Schema must contain uniquely named types but contains multiple types named "QueryRoot"

I manually debugged this and grabbed a snapshot of the schema as returned from Shopify. The schema contains the following things that reference QueryRoot:

{
  "data": {
    "__schema": {
      "queryType": {
        "name": "QueryRoot"
      },
...
        {
          "kind": "OBJECT",
          "name": "QueryRoot",
          "description": "The schema's entry-point for queries. This acts as the public, top-level API from which all queries must start.",
          "fields": [
...
            {
              "name": "query",
              "description": "This field will only resolve once the job is done. Can be used to ask for object(s) that have been changed by the job.",
              "args": [],
              "type": {
                "kind": "OBJECT",
                "name": "QueryRoot",
                "ofType": null
              },

If I remove that last one from the schema and then add this to gatsby-config.js, to manually use this tweaked schema instead of the one we would introspect from Shopify:

    {
      resolve: 'gatsby-source-graphql',
      options: {
        ...,
        createSchema: async () => {
          const schema = await readFile(
            `${__dirname}/shopify-admin-schema-2019-07-modified.json`,
          );
          const json = JSON.parse(schema);
          return buildClientSchema(json.data);
        },

Everything works. So either gatsby is incorrectly trying to parse the Shopify schema, or Shopify is producing an invalid schema. I don't know enough graphql to understand which of those two it is.

If you to workaround this problem, you can either manually modify the schema by removing these lines like so https://www.diffchecker.com/u7Uuj4J5#left-3 or you can just download my version from https://files.terriblefish.com/shopify-admin-schema-2019-07-modified.json and save this alongside gatsby-conf.js and use the code I pasted above to use this modified schema.

Can anyone with more knowledge of Gatsby look at the Shopify schema and tell me if it's valid or if that error about multiple QueryRoot fields is legit?

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contributefor more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

Hey again!

It’s been 30 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it.

Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m HUMAN_EMOTION_SORRY. Please feel free to reopen this issue or create a new one if you need anything else.

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks again for being part of the Gatsby community!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kalinchernev picture kalinchernev  Â·  3Comments

brandonmp picture brandonmp  Â·  3Comments

dustinhorton picture dustinhorton  Â·  3Comments

ferMartz picture ferMartz  Â·  3Comments

Oppenheimer1 picture Oppenheimer1  Â·  3Comments