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 GraphQL 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 GraphQL API's schema as the "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 schema, 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 operations can be performed on it.
Codegen: an overview
Here's how the process works.
- First, we'll install our packages and set up a
generate
command to launch the Code Generator. - Next, we'll create a configuration file that guides the codegen process. At its most basic, this specifies the path to the GraphQL schema, the files in our frontend app it should inspect for GraphQL operations, and where to output the necessary TypeScript types.
- We'll run our codegen command.
- 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 GraphQL 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.
"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.
// TODO
This file should contain a number of instructions that tell the GraphQL Code Generator what we want it to do, such as:
- Where our GraphQL API is running, so it can inspect the schema's types and fields.
- Which of our frontend files it should scan to find all of the GraphQL operations we're using (right now we don't have any!)
- 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.
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 fields in the server's schema.
const config: CodegenConfig = {schema: "http://localhost:4000",};
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
.
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 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.
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 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 operations 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 GraphQL operations 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 GraphQL operations 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 operations 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 GraphQL 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 GraphQL Fragments (more on that in Side Quest: Intermediate Schema Design). 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 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 GraphQL server running on http://localhost:4000
, and the GraphQL Code Generator has generated all the information about the fields they contain and the types of data they are meant to return!
/** 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 operations 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 operation!)
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 GraphQL operations to include), and we'll clear it up in the next lesson!
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.