home/blog/engineering
A colorful mirrored tower
Scott Jones
Scott Jones Senior Software Engineer I
Posted on Feb 8, 2022

GraphQL Codegen: What is it?

#typescript#graphql

Photo by Pablo Titosse on Unsplash

At EF Go Ahead Tours Engineering, we use a tool called graphql-codegen often. Since we use TypeScript and GraphQL, it saves us a lot of time since we can create TypeScript types from our GraphQL schema. Let's set up a basic GraphQL schema to show how it works:

1const typeDefs = gql`
2 type Query {
3 cart(id: ID!): Cart
4 carts: [Cart!]
5 }
6
7 type Cart {
8 items: [Product!]
9 grandTotal: Int!
10 }
11
12 type Product {
13 sku: String!
14 price: Int!
15 }
16`

Pretty simple, we have a two queries and a basic eCommerce model. This sets up our contract with our API. Let's generate our application types. We can do this by installing the needed packages and the plugins for typescript:

1npm i @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers

All of these packages gives us the functionality to generate both types and a typed resolver, with a pretty minimal configuration, as specified in a YAML file. Let's set one up:

1schema: "./src/typeDefs.ts"
2generates:
3 src/generated/types.ts
4 plugins:
5 - typescript
6 - typescript-resolvers

This is an incredibly barebones configuration, only meant to show the TypeScript type generation, more details about configuration can be found here.

All that's left is to run our generator and see what types we are able to use!

1npx graphql-codegen

this can also be a package.json script:

1 "scripts": {
2 "generate": "graphql-codegen"
3 }

at which point the command can be run with npm run generate.

After this, we can see what types are generated in the ./src/generated/typeDefs.ts file! Having this tool is incredibly helpful for both back end and front end work, since our types can be generated from our GraphQL schema, which introduces coupling of the schema definition and the types that we can use within our apps. This makes GraphQL schemas dual-channel, which means that the data and the types of that data is now available for us to use in TypeScript!

Server and client side generation

graphql-codegen works for both server and client side. Let's implement a basic resolver using our newly generated types:

1import { Resolvers } from "./src/generated/types.ts"
2
3export const resolvers: Resolvers => {
4 Query: {
5 cart(parent, args, context) {
6 console.log(args)
7 return null
8 },
9 carts(parent, args, context) {
10 console.log(args)
11 return []
12 }
13 }
14}

Here, we import the Resolvers type from our generated schema, which gives us typechecking for all of our resolvers! This also includes typechecking for our parent type, our field arguments, and our context. Super easy! The package also splits out each individual resolver so one could do:

1// ./src/cartResolvers.ts
2import { CartResolvers } from "./src/generated/types.ts"
3
4export const cartResolvers: CartResolvers => {
5 cart(parent, args, context) {
6 console.log(args)
7 return null
8 },
9 carts(parent, args, context) {
10 console.log(args)
11 return []
12 }
13}
14
15export const resolvers: Resolvers => {
16 Query: {
17 ...cartResolvers
18 }
19}

This makes creating typechecked resolvers with IntelliSense in VS Code very easy and guarantees static types from your schema. How can this tool also help us with the front end? After all, one of the benefits of using GraphQL is that we can query only the data we need. We can do this with a very minimal configuration update in our client side app, copying it from our GraphQL server configuration:

1schema: "./src/typeDefs.ts"
2generates:
3 src/generated/types.ts
4 plugins:
5 - typescript
6 - typescript-resolvers
7+ - typescript-react-apollo
8+ - typescript-operations
9+ - fragment-matcher
10+ documents: "src/graphql/**.{ts, tsx, graphql}"

This configuration will scrape all of our graphql, ts, and tsx files in our src/graphql/ directory and generate types in the src/generated/types.ts file. We'll also need to install the extra plugins that this config requires:

1npm i @graphql-codegen/typescript-operations @graphql-codegen/fragment-matcher @graphql-codegen/typescript-react-apollo

This will allow us to do three things:

  • generate react apollo hooks for our queries and mutations

  • match our fragments to our types

  • generate typescript types for our queries and mutations

At EF Go Ahead Tours we use the first one quite a bit in most typical front-end applications, since we leverage a ton of GraphQL. A typical React component that fetches a cart by ID from our GraphQL server with a search parameter would look like:

1// src/graphql/cart.graphql
2
3query FindCart($id: ID!) {
4 cart(id: $id) {
5 items
6 grandTotal
7 }
8}
9
10// src/checkout/cart.tsx
11import React from "react";
12import { useFindCart } from "./src/generated/typedefs.ts";
13import { Item } from "./Item";
14import { Spinner } from "../shared/Spinner";
15
16export const Cart: React.FC = () => {
17 const cartId = new URLSearchParams(window.location.search).get("cartId");
18 const { data, loading } = useFindCart({
19 variables: {
20 id: cartId ?? ""
21 }
22 });
23
24 if (loading) {
25 return <Spinner />
26 }
27
28 return (
29 <>
30 <h2>{data.grandTotal}</h2>
31 {data?.items.map((item) => <Item item={item} />
32 </>
33 );
34}

Where all of our queries and mutations, along with their variables are typechecked. This makes it very easy to build services alongside clients that consume those services, while maintaining type checking between the both of them. Graphql-codegen is an invaluable tool for us developers here at EF Go Ahead Tours. Without it we would never have been able to build services and front end applications with the speed of sharing types between client and server without the manual work of migrating types and the developer experience that using graphql-codegen affords us.

© EF Education First