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...
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!
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!