Building a type-safe Fullstack Application with GraphQL codegen
There are two approaches to defining your schema in GraphQL: schema-first and code-first. When using Typescript, you might find yourself having to write types again and again. Enters GraphQL codegen: your schema file becomes the single source of truth.
What is GraphQL codegen?
There are two approaches to defining your schema in GraphQL: schema-first and code-first.
In schema-first, you write .graphql
(or .gql
) files while in code-first you write resolver-like types in Javascript (or Typescript).
Now, if you are using Typescript, you might find yourself having to write types again for other purposes - your resolvers for example. That can quickly become a problem, not only because it feels like a waste of time, but also because it makes it hard to maintain. If your codebase and your schema grow in complexity, and you have a whole team working on it, a small type definition update can cause a huge mess!
If we look at a fullstack Typescript application, we have to duplicate our type definitions at least 3 times:
- in the schema file
- for the backend (resolvers)
- for the frontend (GraphQL client)
- BONUS: for the ORM
Enters GraphQL code generator
GraphQL code generator is the solution to that exact problem: you write your schema file and the rest gets generated automatically đź’¨
Now let’s see how it actually works 👇
Example: Building a blog
Let’s take a classic example: building a blog... with Express, GraphQL, React and React Query! (lol, who’s using simple HTML + Markdown these days anyway 🤷‍♂️)
As mentioned above, with this stack we would normally have to:
- write our type definitions in a schema file,
- write types for our backend resolvers,
- write model definitions for our ORM (using Prisma for example),
- write types for React Query on the frontend.
Phew, that is a lot of work!
Now imagine if, 4 months later, we want to add tags to our posts. We would have to go through the same 4 steps to update the types!
But with GraphQL codegen, we have one single source of truth the schema file!
Alright, I hope I teased you enough by now so let’s jump into the code.
Backend with Express and Express GraphQL
If you start from scratch, you can simply install Express, Express GraphQL and Typescript:
$ npm i express express-graphql @graphql-tools/schema cors import-graphql-node
$ npm i -D @types/express
Then we can very easily setup the server:
Note here that I’m using import-graphql-node
to import .graphql
files.
Checkout the repo for more details.
Frontend with React and React Query
We can bootstrap a React project with Typescript very easily thanks to the Create-React-App template:
npx create-react-app client --template typescript
Next, let’s add React Query:
npm i react-query
and set it up:
Setting up GraphQL codegen
Setting up GraphQL codegen is super easy! First, install the CLI:
npm i -D @graphql-codegen/cli
Then launch the initialisation process:
npx graphql-codegen init
This will prompt a series of questions to set it up for your needs. It’s not super important as it’s very easy to update the configuration later.
Here is (approximately) the config file that you’ll end up with:
Let’s go over each field to explain what it does and configure them exactly how we need them.
Schema
This should point to your schema definition. By default, it uses your GraphQL endpoint, but in general, it’s easier to put the path to your actual schema file.
Documents
This is part of the frontend configuration. Documents should point to some schema definition of your operations (queries and mutations). Here’s an example:
The React Query Plugin
The installation process does not have an option for React Query, but we can easily integrate it thanks to the huge plugin hub!
First, we need to install the right plugin:
npm i -D @graphql-codegen/typescript-react-query
Then we configure it in the `codegen.yml` configuration file by adding it to the plugins of the frontend section:
What’s amazing about this plugin is that it’s also going to take care of configuring the React Query client (endpoint, fetcher, etc) so that we can just use simple hooks, e.g., useGetAllPostsQuery()
.
In order to make this work, we need to provide some configuration such as the GraphQL endpoint, but we can also add other things, e.g, an authorization header (with environment variables, how cool is that!):
Putting everything together
We are now ready to go!
To generate the types, we can simply run the command:
npm run codegen
Use the generated types in the backend resolvers:
And use the generated hooks in the frontend like so:
Conclusion
If you decide to go down the code-first route (blue pill) good for you, but many teams decide to pick a schema-first approach to build their GraphQL API, and even though it’s a great option it can quickly become a burden to test and maintain your code.
But fortunately, graphql-codegen is an elegant solution to remediate to that problem of code duplication, making the schema file your single source of truth!
Wanna know more about DevSecOps? Check out our blog post "9 GraphQL Security Best Practices" and learn how to build safe GraphQL APIs.
GraphQL Security
As explained in a previous post, GraphQL has no security in place by default. We found that most GraphQL APIs are hence subject to the most basic attacks (brute force, DoS, etc).
That's why we built Escape GraphQL Security Platform! Start monitoring the security of your endpoints today with a 14-day free trial