0. Codegen
2m

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 !

Right now, our frontend app doesn't know anything about the we wrote in our server folder. 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, after all, that a Track has text for a title, and an Author has text for a name, and so on - but if we change our 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 backend we've already written as our "single source of truth" for all of the types we could possibly query 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 GraphQL Code Generator.

@graphql-codegen/cli is one such tool that can read in a GraphQL , 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 s can be performed on it.

@graphql-codegen/cli

Let's get started with @graphql-codegen/cli 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

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!

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 GraphQL Code Generator what we want it to do, such as:

  1. Where our GraphQL API is running. Here, it retrieves information from the GraphQL API about the types and s that exist in its .
  2. Which of our frontend files it should scan to find all of the GraphQL s we're using (right now we don't have any!)
  3. Where it should output the types it generates. The GraphQL 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 GraphQL server's endpoint, http://localhost:4000. The GraphQL Code Generator will look at this address and read the types and s in the server's .

import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "http://localhost:4000",
};
export default config;

The documents property

Next, we'll define the documents that GraphQL 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.

import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "http://localhost:4000",
documents: ["src/**/*.tsx"],
};
export default config;

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.

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

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 Apollo Client. 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.

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

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 GraphQL strings.

We could use the same gql tagged template literal from graphql-tag here as well. But we'll benefit more from using GraphQL 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 GraphQL s 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.

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

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

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

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 GraphQL s 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 GraphQL Code Generator not to worry if it doesn't find any GraphQL s 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:

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;

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

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

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

The first file fragment-masking.ts contains some functions that will aid us when working with GraphQL s (more on that in a later course!). The second file, gql.ts, contains the gql function we asked GraphQL Code Generator to create to help parse GraphQL queries so they can be used by a GraphQL client, like Apollo Client.

When we jump into graphql.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 !

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

src/__generated__/graphql.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 GraphQL s on the frontend yet, we can see how GraphQL 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 GraphQL 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 GraphQL !)

import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "http://localhost:4000",
documents: ["src/**/*.tsx"],
generates: {
"./src/__generated__/": {
preset: "client",
presetConfig: {
gqlTagName: "gql",
},
},
},
};
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 GraphQL s 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.