How to generate TypeScript types and React hooks from GraphQL queries

Captain's log, stardate d317.y39/AB

We have been working with Shopify for a good while now, for all kinds and sizes of e-commerce sites, but we've been lately experimenting with GraphQL and React-based frontend applications to enhance user experience to unprecedented levels, so we've compiled some guidelines for you.

Earth from space - Photo by NASA, borrowed from Unsplash

I have been testing Shopify's GraphQL API in one of our biggest e-commerce projects. It is proving so far to be a great opportunity to learn more about Apollo in React, and the Shopify storefront API documentation is great, easy to understand, and follow.

Additionally, I'm using the GraphQL Code Generator, a tool that takes GraphQL queries as input and generates code to deal with the API. It saves a lot of coding time.

Let me explain briefly how to generate TypeScript types and React hooks from GraphQL queries.

I will start with showing some code samples that we're using to connect to Shopify's Storefront API. At first, I considered running Apollo CLI to generate types, as it has an integrated tool to do it, but the lack of good samples and the ease of use of the GraphQL Code Generator changed my mind.

The process is the following:

  • Write your queries/mutations.
  • Configure the generator.
  • Download the API's GraphQL schema and run the generator.

Write your queries/mutations.

You have multiple options when placing the GraphQL queries inside your app. My preference (absolutely opinionated) is to move GraphQL queries, fragments, and mutations into separate files under a graphql directory, mostly because of future reuse and general cleanliness. When writing a React component you don't need to know how to retrieve the data, you should abstract that, and later you can reuse the same query/fragment.

In this PoC (proof of concept) app, I created the following hierarchy. This is a similar approach to React's API files and route files. For instance:

Project folder organisation

What follows is the content of products.graphql:

    query products($first: Int) {
      products (first: $first) {
        edges {
          node {
            id
            title
          }
        }
      }
    }

Configure the generator

We are downloading and generating the code at the same time, so first, we need to configure the generator. We're creating a codegen.js file that will feed it. Inside, we're holding the API information and some lib options. I'm using the dotenv lib to retrieve .env properties.

    require("dotenv").config();

    module.exports = {
      schema: [
        {
          [process.env.REACT_APP_API]: {
            headers: {
              "X-Shopify-Storefront-Access-Token":
                process.env.REACT_APP_ACCESS_TOKEN,
            },
          },
        },
      ],
      documents: ["react/graphql/**/*.graphql"],
      overwrite: true,
      generates: {
        "/react/graphql/graphql-types.ts": {
          plugins: [
            "typescript",
            "typescript-operations",
            "typescript-react-apollo",
          ],
          config: {
            withHooks: true,
          },
        },
      },
    };

The important bits:

  • schema: API's endpoint information with auth headers.
  • documents: where to look in our app when searching for queries.
  • generates: where to save the types, plugins to use, and plugin options.

The typescript-react-apollo plugin can generate React hooks for the queries we already have, we enable it with the withHooks property.

The generator will output type info and hooks only for the current queries we have, not for the entire API, that's why it needs the documents property.

Download the API's GraphQL schema and run the generator

Running:

    graphql-codegen --config=codegen.js

Creates the graphql-types.ts file (that's the location we specified in the codegen.js file). It has the type information and hooks implementations for the queries we have.

In our React + Apollo app, we get the products information, typed.

    const { data, loading, error } = useProductsQuery({
        variables: {
          first: 100
        },
      });

    data?.products.edges // --> type of Product[]

That's all!

Sources

That's all from me! Hope you find this useful and feel free to drop us a line in the comments below if you need any help with this!

David Garmendia

David Garmendia

Our JavaScript (Angular, React, Node.js & Ionic) specialist David hails from Asturias. He moved to Barcelona when we hired him only to find out that he would not find an office here because the company is 100% remote.

comments powered by Disqus

You're one step away from meeting your best partner in business.

Hire Us