0. Codegen
10m

Generating types

There's one important step that we need to take care of before proceeding with our TypeScript app - we need to generate the TypeScript types to represent all of the GraphQL types in our schema!

Why we need codegen

Right now, our frontend app doesn't know anything about the schema that our backend API is using. But because we're going to be writing queries for Track and Author data, we need the frontend to understand what type of data they involve. We could write out the TypeScript types manually—we know that a Track has text for a title, and an Author has text for a name, and so on—but if we change our schema in the future, we have to remember to update our frontend as well; this means that our frontend's TypeScript types can easily get out of sync, if we're not careful!

Instead, we can look to the API's schema as the "single source of truth" for all of the types we could possibly on the frontend. An easy way to do this, and to keep our frontend's type definitions consistent with the backend, is to use a Code Generator.

@graphql-codegen/cli is one such tool that can read in a , compare it against the queries we're asking our frontend code to run, and generate all of the types that we'll need to use on the frontend. As we work on new features, we'll benefit from the clarity TypeScript gives us about what data exists on each type and what kinds of can be performed on it.

Codegen: an overview

Here's how the process works.

  1. First, we'll install our packages and set up a generate command to the Code Generator.
  2. Next, we'll create a configuration file that guides the codegen process. At its most basic, this specifies the path to the , the files in our frontend app it should inspect for GraphQL , and where to output the necessary TypeScript types.
  3. We'll run our codegen command.
  4. Finally, we'll check out the types it generated for us!

Ready to get started? Let's go!

Step 1: Installing @graphql-codegen/cli

Let's get started with the Code Generator by installing it in our client folder. We'll also install @graphql-codegen/client-preset, which gives us some React-specific configuration out of the box.

We only need these packages during development, so we'll install them under devDependencies.

npm install -D @graphql-codegen/cli @graphql-codegen/client-preset @graphql-codegen/typescript @graphql-codegen/typescript-operations

When the installation is complete, open up client/package.json. Under the scripts key, we'll add a new command, generate, that will call the @graphql-codegen/cli package.

client/package.json
"scripts": {
"test": "vitest",
"start": "vite",
"build": "vite build",
"generate": "graphql-codegen"
},

If we run the command right away with npm run generate, we'll see that we get an error—this is because we haven't yet created the codegen.ts file that the Code Generator looks for.

Error: Unable to find Codegen config file!
Please make sure that you have a configuration file under the current directory!

Step 2: Defining the configuration file

Let's define codegen.ts right in the root of our client folder.

client/codegen.ts
// TODO

This file should contain a number of instructions that tell the Code Generator what we want it to do, such as:

  1. Where our API is running, so it can inspect the schema's types and .
  2. Which of our frontend files it should scan to find all of the we're using (right now we don't have any!)
  3. Where it should output the types it generates. The Code Generator will use all of the information we gave it to output generated types in a folder of our choosing.

We'll start by creating a new object called config and exporting it.

client/codegen.ts
const config = {};
export default config;

Next, let's import the type definition we'll use for the config object. @graphql-codegen/cli provides one out of the box for us.

import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {};
export default config;

Within the config object, we'll define three properties: schema, documents, and generates.

The schema property

For the schema property, we need to pass in our 's endpoint, http://localhost:4000. The Code Generator will look at this address and read the types and in the server's schema.

const config: CodegenConfig = {
schema: "http://localhost:4000",
};

The documents property

Next, we'll define the documents that Code Generator should consider when generating types for our frontend. All of our code is contained in the src folder, and we want to be sure that it looks for files in all of the src subfolders as well! Finally, we should set in the configuration that we want to scan only files that end with .tsx.

const config: CodegenConfig = {
schema: "http://localhost:4000",
documents: ["src/**/*.tsx"],
};

The generates property

The last thing the Code Generator needs to know is where to output all of the code that it generates. For this, we'll ask it to create a new folder called __generated__ under src. We set this as a key, whose value is an object with some additional configuration.

const config: CodegenConfig = {
schema: "http://localhost:4000",
documents: ["src/**/*.tsx"],
generates: {
"./src/__generated__/": {
// TODO
},
},
};

Here we can make use of the @graphql-codegen/client-preset package that we installed earlier. This package configures the Code Generator to work well with React apps, particularly those using . It also gives us some additional plugins that we can tweak to modify how the Code Generator behaves.

We'll add the client preset indicator to the object, as shown below.

generates: {
"./src/__generated__/": {
preset: "client",
},
},

Customizing the graphql function

One other piece of the generated code we want to control is the name of the graphql function the Code Generator will give us to work with our queries. Recall that on the server side, we used the tagged template literal gql from the graphql-tag library to prepare our strings.

We could use the same gql tagged template literal from graphql-tag here as well. But we'll benefit more from using Code Generator's function instead; in addition to giving us the same functionality as the gql utility we've used before, it comes with an understanding of all the types that the Code Generator produces - meaning that we don't have to type them ourselves! We can simply use the generated function, and it does all the work for us, looking up the types for the we pass to it.

By default, the Code Generator exports this function as graphql. But to maintain the naming convention of gql, we have the option to rename it. We can do this by setting an additional property below preset called presetConfig.

generates: {
"./src/__generated__/": {
preset: "client",
presetConfig: {
// TODO
},
},
},

This is an object where we can set a gqlTagName property, along with our preferred name for this function, gql.

presetConfig: {
gqlTagName: "gql",
},

Additional plugins

A recent release of the GraphQL Code Generator library makes it necessary for us to define additional plugins separate from our initial client preset.

Remember those additional plugins that we installed at the start of the lesson? Those plugins will be responsible for generating the code for all of the types in our schema, as well as the different we use throughout our frontend app.

Outside of the object defined after "./src/__generated__/", we'll define a new file path and a second config object. This file will contain all of our schema's generated types, so we'll call it types.ts, and we'll ask for it to be generated in the same __generated__ folder.

generates: {
"./src/__generated__/": {
preset: 'client',
presetConfig: {
gqlTagName: "gql"
}
},
"./src/__generated__/types.ts": {
// TODO
},
},

Inside of this object, we can define a plugins key. Here, we'll specify an array containing the plugins "typescript" and "typescript-operations".

"./src/__generated__/types.ts": {
plugins: ["typescript", "typescript-operations"],
}

And that's it for our plugins and types.ts file! Let's generate some files.

Step 3: Running GraphQL Code Generator

If we run our npm run generate command now, we'll see an error: that's because currently our frontend code doesn't contain any for the Code Generator to scan.

Unable to find any GraphQL type definitions for the following pointers:
- src/**/*.tsx

To run the command anyway, and see what the Code Generator outputs by default for us, we can pass an additional flag into our config object called ignoreNoDocuments. By setting ignoreNoDocuments to true, we're telling Code Generator not to worry if it doesn't find any GraphQL in our frontend code; it should proceed with outputting its default generated code anyway. That will give us a chance to take a look at what else it generates for us!

Add ignoreNoDocuments: true to your config object, as shown below:

const config: CodegenConfig = {
schema: "http://localhost:4000",
documents: ["src/**/*.tsx"],
generates: {
"./src/__generated__/": {
preset: "client",
presetConfig: {
gqlTagName: "gql",
},
},
},
ignoreNoDocuments: true,
};

Let's run our generate command again in the client folder. (Make sure that your server is still running on http://localhost:4000!)

npm run generate

If all goes well, we'll see some green checkmarks in the terminal, along with a new folder under src called __generated__!

Step 4: Inspecting the output

Let's look at the files that the Code Generator created for us.

📂 __generated__
┣ 📄 fragment-masking.ts
┣ 📄 gql.ts
┣ 📄 graphql.ts
┣ 📄 index.ts
┗ 📄 types.ts

The first file fragment-masking.ts contains some functions that will aid us when working with (more on that in Side Quest: Intermediate Schema Design). The second file, gql.ts, contains the gql function we asked Code Generator to create to help parse GraphQL queries so they can be used by a , like .

When we jump into types.ts, however, we'll see something really interesting - towards the bottom of the file, we can find the three types that we defined in our backend schema!

Query, Track, and Author have successfully been introspected from our running on http://localhost:4000, and the Code Generator has generated all the information about the they contain and the types of data they are meant to return!

src/__generated__/types.ts
/** Author of a complete Track or a Module */
export type Author = {
__typename?: "Author";
id: Scalars["ID"]["output"];
name: Scalars["String"]["output"];
photo?: Maybe<Scalars["String"]["output"]>;
};
export type Query = {
__typename?: "Query";
/** Get tracks array for homepage grid */
tracksForHome: Array<Track>;
};
/** A track is a group of Modules that teaches about a specific topic */
export type Track = {
__typename?: "Track";
author: Author;
id: Scalars["ID"]["output"];
length?: Maybe<Scalars["Int"]["output"]>;
modulesCount?: Maybe<Scalars["Int"]["output"]>;
thumbnail?: Maybe<Scalars["String"]["output"]>;
title: Scalars["String"]["output"];
};

Even though we haven't written any on the frontend yet, we can see how Code Generator was able to pull information about the type of data our frontend will be working with.

From now on, we want to know if there aren't any documents for the Code Generator to scan, so we can clean up our codegen.ts file by removing the ignoreNoDocuments flag. (Running npm run generate will still produce an error, but we'll take care of that in the next lesson by adding our first !)

import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "http://localhost:4000",
documents: ["src/**/*.tsx"],
generates: {
"./src/__generated__/": {
preset: "client",
presetConfig: {
gqlTagName: "gql",
},
},
},
- ignoreNoDocuments: true,
};
export default config;

We're ready to start sending queries!

Note: At this point, you might see an error where your client application is running on http://localhost:3000! The Variable 'documents' implicitly has an 'any[]' type error message refers to our generated code (which didn't find any to include), and we'll clear it up in the next lesson!

Next

Share your questions and comments about this lesson

Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.