Type-safe Shopify GraphQL client

Oct 22, 2025

If you're building a headless ecommerce site using Shopify, chances are you'll want to use their GraphQL API.

The documentation on how to best do this, combined with the plethora of seemingly discontinued official and unofficial SDKs makes it pretty confusing for newcomers.

In this guide, I'll show you how you can create your own type-safe Shopify GraphQL client using GraphQL Codegen and graphql-request.

Setup your documents

First, let's create a directory to store your fragments, queries and mutations. In this guide, we'll be storing these as .graphql files, which is nicer to work with than queries written as template literals using the gql tag.

To write and test queries, you can use the GraphiQL explorer. Once your query is working, you can paste it into the relevant .graphql file.

It's a good idea to name the file the same way you would the query. For example, a getProductByHandle query should live inside getProductByHandle.graphql.

Fragments are also worth using here. We will be configuring our codegen to generate types from these, which you can then use throughout your code.

Here's a simplified example:

graphql
# /fragments/ProductFields.graphql
fragment ProductFields on Product {
  id
  title
  handle
  variants(first: 10) {
    edges {
      node {
        ...ProductVariantFields
      }
    }
  }
}

# /fragments/ProductVariantFields.graphql
fragment ProductVariantFields on ProductVariant {
  id
  title
}

# /queries/getProductByHandle.graphql
query getProductByHandle($handle: String!) {
  productByHandle(handle: $handle) {
    ...ProductFields
  }
}

Codegen will generate types from these that can then be used like this:

tsx
const ProductCard = (props: { product: ProductFieldsFragment }) => {
  const {
    product: { title },
  } = props;

  return <></>;
};

Regarding the directory structure, separating documents out into different folders is a good idea:

├─ src/
│  ├─ lib/
│  │  ├─ shopify/
│  │  │  ├─ graphql/
│  │  │  │  ├─ generated/
│  │  │  │  ├─ queries/
│  │  │  │  ├─ fragments/
│  │  │  │  ├─ mutations/

Setup your Shopify storefront API token

  1. Login to your Shopify admin

  2. Go to Settings, Apps & sales channels

  3. Click the 'Develop apps' button at the top of the screen, then 'Create an app'

  4. After creating the app, you will be taken to the app's configuration page. Click 'Configure Storefront API scopes' under the API credentials tab, enable the relevant permissions, and click 'Save'.

  5. Go back to the API credentials tab in the app, and grab your token from there.

You will also need the base URL. The format looks like this:

bash
https://<store_handle>.myshopify.com/api/<version>/graphql.json

The store_handle can be found by looking at the URL bar when logged in – it's the bit that comes after the /store segment. Available versions can be found on the GraphiQL explorer page. At the time of writing, the latest stable version is 2025-10.

An example URL for a store with handle recharge and version 2025-10 would look like this:

bash
https://recharge.myshopify.com/api/2025-10/graphql.json

Configure codegen

The GraphQL Codegen tool has various plugins that let you generate TypeScript types and client code from a GraphQL schema.

Start by installing the relevant dependencies, which are as follows:

bash
pnpm i @graphql-codegen/cli @graphql-codegen/import-types-preset @graphql-codegen/typescript-graphql-request -D

GraphQL Codegen provides an init function you can use to start a wizard, but for the purpose of this article you can skip that and create a codegen.ts file with the following code. Remember to update the URL and token. You may also want to update the path for where the documents are located.

ts
import type { CodegenConfig } from "@graphql-codegen/cli";

const root = "src/lib/shopify/graphql";

const config: CodegenConfig = {
  overwrite: true,
  schema: {
    "https://<store_handle>.myshopify.com/api/<version>/graphql.json": {
      headers: {
        "X-Shopify-Storefront-Access-Token": "<your_access_token>",
      },
    },
  },
  documents: `${root}/**/*.graphql`,
  generates: {
    [`${root}/generated/types.ts`]: {
      plugins: ["typescript"],
    },
    [`${root}/generated/sdk.ts`]: {
      preset: "import-types",
      presetConfig: {
        typesPath: "./types",
      },
      plugins: ["typescript-graphql-request"],
    },
  },
};

export default config;

This will:

  1. Introspect the schema using the specified Shopify endpoint
  2. Validate your fragments, queries and mutations against the schema, generate the types and place them inside /generated/types.ts
  3. Uses those types to generate an SDK that will be written to /generated/sdk.ts

To run it, do:

bash
pnpm run graphql-codegen --config codegen.ts

You'll probably want to add this to your package.json as shopify:codegen:storefront or similar, e.g:

json
{
  "scripts": {
    "codegen": "graphql-codegen --config codegen.ts"
  }
}

Setup your request client

Install graphql-request. There are other libraries you might want to consider, like Apollo, or URQL among others, however in practice I have found graphql-request is simple to setup and does the job without too much hassle.

bash
pnpm i graphql-request

You can now create a request client instance, and then wrap the getSdk method exported by your sdk.ts file around it like so:

ts
const client = new GraphQLClient("https://<store_handle>.myshopify.com/api/<version>/graphql.json", {
  headers: {
    "X-Shopify-Storefront-Access-Token": "<storefront_access_token>",
  },
});

export const shopifyStorefrontClient = getSdk(client);

And that's it! You can now use your client like this:

ts
const product = await shopifyStorefrontClient.getProductByHandle({handle: "test"})

Bonus: using @tanstack/query

If you want to create client-side hooks, you can wrap your newly created client inside useQuery like so:

tsx
import { useQuery } from '@tanstack/react-query'
import { shopifyStorefrontClient } from './path-to-your-client'

export const useProduct = (handle: string) => {
  return useQuery({
    queryKey: ['product', handle],
    queryFn: () => shopifyStorefrontClient.getProductByHandle({ handle }),
    enabled: !!handle,
  })
}

Alternatively, you can use the typescript-react-query codegen plugin to automatically generate these hooks for you during the code generation process.

More articles