With GraphQL Codegen

Supercharge your development experience in TypeScript projects with Nacelle Storefront SDK 2.x and GraphQL Codegen

GraphQL Codegen x Nacelle

What is GraphQL Codegen?

GraphQL Code Generator is a powerful tool that automates the creation of TypeScript types related to GraphQL operations. Key benefits include:

  • Strong Typing: GraphQL Codegen generates TypeScript type definitions based on your GraphQL schema. This ensures type safety throughout your codebase, reducing the chances of runtime errors and improving developer experience.
  • Automatic Code Generation: With GraphQL Codegen, you can automatically generate TypeScript types based on your GraphQL schema and the GraphQL queries used in your project. This saves development time and keeps your frontend and backend code in sync.
  • Improved Productivity: GraphQL Codegen significantly boosts developer productivity in TypeScript projects. It reduces the time spent on writing repetitive code, improves type safety, and enables seamless integration with existing workflows, resulting in faster development cycles.
  • Consistency: By generating code from a central source of truth, i.e., your GraphQL schema, GraphQL Codegen ensures consistency across different parts of your TypeScript codebase. Any changes made to the schema or operations trigger regeneration of code, making it easy to keep your code up-to-date.
  • Boilerplate Reduction: GraphQL Codegen eliminates repetitive code by automatically generating code for data fetching, serialization, and deserialization. This reduces manual effort and helps maintain a clean and concise codebase.

How to configure GraphQL Codegen

There are a number of ways to configure GraphQL Codegen. To understand the art of the possible, we recommend reading the GraphQL Code Generator documentation. Below is an example setup that will work well with TypeScript projects that use Nacelle Storefront SDK version 2.x.

Prepare our code editor

If using VSCode, we'll want to install the GraphQL for VSCode extension. This will provide syntax highlighting and autocomplete for GraphQL queries.

Install dependencies

Assuming that TypeScript is already installed in the project, the only devDeps that we'll need are related to GraphQL Codegen and GraphQL Config.

npm i -D @graphql-codegen/cli @graphql-codegen/client-preset graphql graphql-config

Next, we'll install Nacelle Storefront SDK 2.x as a regular dependency:

npm i @nacelle/storefront-sdk@latest

Create a GraphQL Codegen config file

Because we installed GraphQL Config alongside GraphQL Codegen, we can rely on a single configuration file to manage both tools. Create a file called graphql.config.json in the root of the project and add the following:

{
	"schema": "<your-nacelle-storefront-api-endpoint>",
	"documents": "src/**/*.{graphql,js,ts,jsx,tsx}",
	"extensions": {
		"codegen": {
			"ignoreNoDocuments": true,
			"generates": {
				"./src/gql/": {
					"preset": "client"
				}
			}
		}
	}
}

Write a GraphQL query

In your project's src directory, create a named GraphQL query with the /* GraphQL */ comment at the beginning of a tagged template literal. Be sure to give the query a unique name. For example:

/* GraphQL */ `
  query FirstTenProducts {
    allProducts(filter: { first: 10 }) {
      edges {
        node {
          nacelleEntryId
        }
      }
    }
  }
`;

Run GraphQL Codegen

Let's begin by adding the following scripts to the package.json file:

{
	"scripts": {
		"codegen": "graphql-codegen",
		"codegen:watch": "graphql-codegen --watch"
	}
}

Next, we'll run the following command to generate TypeScript types for our GraphQL query:

npm run codegen

We can supercharge our development workflow by running the codegen in watch mode, so that the generated types are updated as we create and update our GraphQL queries:

npm run codegen:watch

Use the generated types

After Codegen has run for the first time, we'll have a graphql function that's exported from src/gql/index.ts. This function accepts a GraphQL query and returns a Promise that resolves to the query's response data. Let's wrap our earlier GraphQL query with this new graphql function:

import { graphql } from './gql';

const ProductsDocument = graphql(/* GraphQL */ `
	query FirstTenProducts {
		allProducts(filter: { first: 10 }) {
			edges {
				__typename
				node {
					nacelleEntryId
				}
			}
		}
	}
`);

Get fully-typed data from the Nacelle Storefront SDK

Nacelle Storefront SDK 2.x's query method accepts the output of the GraphQL Codegen graphql function. Let's use the ProductsDocument query that we created earlier to create a typesafe response:

import { StorefrontClient } from '@nacelle/storefront-sdk';
import { graphql } from './gql';

const client = new StorefrontClient({
	storefrontEndpoint: '<your-nacelle-storefront-api-endpoint>'
});

const ProductsDocument = graphql(/* GraphQL */ `
	query FirstTenProducts {
		allProducts(filter: { first: 10 }) {
			edges {
				__typename
				node {
					nacelleEntryId
				}
			}
		}
	}
`);

const { data: productsData } = await client.query({
	query: ProductsDocument
});

Our development environment is now fully aware of the type of the productsData returned by our GraphQL query. This gives us a great autocomplete experience:

A screenshot of TypeScript providing suggestions of object properties to access within the `productsData` that we queried with the Storefront SDK

If we try to provide this data to a function or UI component that expects a different type, our editor will warn us of the mismatch.

Enriching GraphQL queries with suggested fields

Because we've also configured GraphQL Config, we can enjoy suggestions and autocomplete for our GraphQL queries in our code editor. For example:

A screenshot of a GraphQL query in VSCode. It includes autocomplete suggestions for additional fields that aren't yet being queried for.

Enhancing type accuracy with __typename in typedFields

Nacelle Storefront GraphQL API users with access to Typed Fields can take advantage of more precise data fetching with smaller data payloads. Using the Storefront SDK with GraphQL Codegen offers a stellar Typed Fields querying experience. But to get the most out of the Typed Fields experience in a TypeScript project, it’s important to know how to effectively leverage __typename to unlock a smooth and typesafe development experience.

Using __typename for type narrowing

As described in the GraphQL spec, we can request the __typename of any object in our query. The __typename property acts as a type identifier, enabling you to deal with union types contained in Content.typedFields. Let’s take a look at an example.

Example

Let's explore a practical application using a codegen-driven query:

import { StorefrontClient } from "@nacelle/storefront-sdk";
import { graphql } from "./gql";

const client = new StorefrontClient({
  storefrontEndpoint: "<my-nacelle-storefront-graphql-endpoint>"
});

const TypedFieldsQueryDocument = graphql(/* GraphQL */ `
  query TypedContentQuery($filter: ContentFilterInput) {
    allContent(filter: $filter) {
      edges {
        node {
          nacelleEntryId
          typedFields {
            __typename # <-- IMPORTANT!
            ... on FullSnackRecipeClubArticleFields {
              relatedArticles {
                edges {
                  node {
                    title
                  }
                }
              }
            }
            ... on FullSnackRecipeClubBasicTasteFields {
              handle
              name
              relatedRecipe {
                handle
              }
            }
          }
        }
      }
    }
  }
`);

const { data: contentData } = await client.query({
  query: TypedFieldsQueryDocument,
  variables: { filter: { first: 10 } },
});

Let’s consider the value of contentData?.allContent.edges.map((edge) => edge.node). This is an array of Content data, but because we’ve specified Content Type-specific fields of interest with Inline Fragments, our nodes' typedFields data could either be of type MyCmsArticleFields or MyCmsBasicTasteFields. Without leveraging __typename in a conditional block, TypeScript can’t know whether a node’s typedFields correspond to MyCmsArticleFields or MyCmsBasicTasteFields. Let’s take a look at how to leverage __typename to unlock a fully typesafe experience:

contentData?.allContent.edges.forEach(({ node }) => {
  if (node.typedFields?.__typename === "FullSnackRecipeClubArticleFields") {    
    // now we have access to `typedFields` that are
    // specific to our `article` content type's fields
    node.typedFields.relatedArticles?.edges.map(({ node }) => node?.title);
  }
});

Using this technique in our TypeScript x GraphQL Codegen x Nacelle Storefront SDK project ensures our code is typesafe and enhances developer experience with powerful autocomplete.

Summary

By generating TypeScript types based on your GraphQL schema and queries, GraphQL Codegen ensures type safety throughout your codebase, reduces the chances of runtime errors, and improves developer productivity. By taking the time to configure GraphQL Codegen and GraphQL Config in your TypeScript project, you'll be able to enjoy long-lasting benefits of strong typing and automatic code generation.