Shopify: Getting to grips with GraphQL

Intro

The Shopify GraphQL documentation can be a lot to get to grips with, even if you adopt an approach I recommend.

So what can make it easier for us, especially keeping up to date with the new versions and changes...

Linting!

This linting is designed to work with eslint, which is very commonly used in the JavaScript world.

To use this you should have an eslint.config.js file.

Building ontop of the great work by @graphql-eslint/eslint-plugin, I've created a new eslint plugin to add some Shopify GraphQL specific best practices!

This means you can check your GraphQL for unknown fields, deprecations when migrating between versions and best practices around mutations and pagination for example.

With Shopify Codegen

The linters work best combined with knowledge about Shopify GraphQL Schema.

If you are already using Shopify GraphQL Codegen, this does a lot of the work for us and is our recommended approach.

In your .graphqlrc.ts or equivalent JS file, ensure that pluckConfig is exposed in the default extensions object.

This will allow the linters to pick up the GraphQL code from your codebase using the same prefix as the codegen tools, it also uses the schemas from here as well.

Here is an example .graphqlrc.ts file, of how to expose this pluckConfig.

However as long as the path projects.default.extensions.pluckConfig exists in your config it will be used, so you can config this how you want to.

import { shopifyApiProject, ApiType } from '@shopify/api-codegen-preset';

const apiVersion = '2025-04';
const config = shopifyApiProject({
  apiType: ApiType.Admin,
  apiVersion: apiVersion,
  documents: ['./**/*.{js,ts,jsx,tsx}'],
  outputDir: './shopify/types',
})

export default {
  schema: `https://shopify.dev/admin-graphql-direct-proxy/${apiVersion}`,
  documents: ['./**/*.{js,ts,jsx,tsx}'],
  projects: {
    default: {
      ...config,
      extensions: {
        ...config.extensions,
        pluckConfig: config.extensions?.codegen?.pluckConfig,
      },
    },
  },
};

Without Codegen

If you are not using the codegen tools, you can manually define the schema in your eslint.config.js under parserOptions or add an equivalent Graphql Config file which will automatically get picked up.

Here is an example where I'm passing the schema information in parserOptions.

import js from '@eslint/js';
import graphqlPlugin from '@graphql-eslint/eslint-plugin';

export default [
  {
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlPlugin.parser,
      parserOptions: {
        graphQLConfig: {
          schema: `https://shopify.dev/admin-graphql-direct-proxy/2025-04`,
          documents: [['./**/*.{js,ts,jsx,tsx,graphql}']],
        },
      },
    },
    plugins: {
      '@graphql-eslint': graphqlPlugin,
      'pimsical-shopify-graphql': shopifyGraphqlPlugin,
    },
    rules: {
      ...graphqlPlugin.configs['flat/operations-recommended'].rules,
      '@graphql-eslint/require-selections': 'off',
      ...shopifyGraphqlPlugin.configs['flat/recommended'].rules,
    },
  },
];

eslint config

Now we've got our codegen and schema information prepared we can configure the linting!

Installation

Get all the packages installed.

npm i -D eslint @eslint/js @graphql-eslint/eslint-plugin eslint-plugin-pimsical-shopify-graphql

Queries in code

If your GraphQL queries are located in your code in JavaScript or TypeScript files, the linter can parse them like so:

import graphqlPlugin from '@graphql-eslint/eslint-plugin'
import shopifyGraphqlPlugin from 'eslint-plugin-pimsical-shopify-graphql';

export default [
  // any other linting config you wanted to have
  {
    files: ['**/*.{ts,js}'],
    processor: graphqlPlugin.processor
  },
  {
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlPlugin.parser
    },
    plugins: {
      '@graphql-eslint': graphqlPlugin,
      'pimsical-shopify-graphql': shopifyGraphqlPlugin,
    },
    rules: {
      ...graphqlPlugin.configs['flat/operations-recommended'].rules,
      '@graphql-eslint/require-selections': 'off',
      ...shopifyGraphqlPlugin.configs['flat/recommended'].rules,
    },
  },
];

This will use the pluckConfig we defined earlier to extract the GraphQL queries out of the strings and into temporary .graphql files so they can be linted and checked, using the processor in the config, like magic!

This will then parse them with the graphqlPlugin parse to understand the format and begin linting them with both the plugins and the rules listed below.

You can always configure these rules of either plugin how you'd like it to work, but above is our recommended approach.

Queries in .graphql files

If your queries are already in .graphql files then you don't need the processor from above. So your config can look like:

import graphqlPlugin from '@graphql-eslint/eslint-plugin'
import shopifyGraphqlPlugin from 'eslint-plugin-pimsical-shopify-graphql';

export default [
  // any other linting config you wanted to have
  {
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlPlugin.parser
    },
    plugins: {
      '@graphql-eslint': graphqlPlugin,
      'pimsical-shopify-graphql': shopifyGraphqlPlugin,
    },
    rules: {
      ...graphqlPlugin.configs['flat/operations-recommended'].rules,
      '@graphql-eslint/require-selections': 'off',
      ...shopifyGraphqlPlugin.configs['flat/recommended'].rules,
    },
  },
];

Run

Now you can run your linting! Use whatever command you already have or npx eslint .

Any deprecated fields will be listed along with any other schema issues and best practices. Helping keep on top of Shopify GraphQL!

Example of eslint result with errors in GraphQL queries

Conclusion

I hope that this can help keep on top of GraphQL changes, updates and best practices. Making it a little easier to work with GraphQL going forwards.

If you have any suggestions for best practices or if there is something tripping you up in GraphQL, I'd love to hear about them and you can raise an issue on Github as this project is open source!

Learn More

These are webmentions powered by webmention.io