Flow is open-sourced by Facebook and we are able to perform type checking with a Flow server while coding. On the other hand, TypeScript is maintained by Microsoft. TypeScript is older then Flow and seems to me that the ecosystem is much better. TypeScript has better support with typings for more libraries, especially on the backend. In this article we will use solely TypeScript in our examples.
Model example of manual static typing for GraphQL queries
Let’s first take a look at how to define our static typings manually. We will start with this simple schema:
We would like to fetch the list of subscribed users. If you have your development server running you can move to GraphQL Playground. We can then execute the following GraphQL document:
Now if you use our example repository. Let's say we would like to include generate our TypeScript types each time we change our GraphQL schema and propagate these changes to your development workflow, so that you can use it directly in your frontend components We can execute this query in GraphiQL and we will receive something like this
Then we will start writing our TypeScript type definitions. We will first need to manually check the schema so that our definitions are in sync with the data from the GraphQL server. We can write the definition for Subscriptions query as follows:
We need to manually check our schema to see what each type represents so that our static typings are in sync. Let’s say we want to add the required field source that will be typed as an enum value. The updated Subscription type in SDL (Schema Definition Language) will then be as follows:
In order to fetch this field we will need to update our GraphQL query as well:
But what about our typings? We need to update the affected typings wherever they are being used. I believe that the biggest trade off for static typing is the increased time for development, the duplication of data structure and the possible friction that can occur with versioning our APIs. We cannot only update our code; we also need to add our typings manually and then update them after each change. It can lead to wrong typings and false errors if developers do not sync up immediately. These problems can be solved with automatic generation of types with GraphQL. Our GraphQL gateway will serve as our single source of truth, and static typing will be synced immediately on both the frontend and backend.
How would we achieve that with GraphQL?
So now that we have talked about adding typings in our TypeScript code manually, how can GraphQL help us automate that? As we mentioned, one of the biggest problems when defining typings is that the manual static typing can get too time-consuming and it is hard to keep everything in sync through versioning. We could already notice the connection between GraphQL type system and either the TypeScript or Flow type systems. GraphQL’s type system is strongly typed, and we can perform transformations from GraphQL type system to TypeScript type systems.
To get a better idea of how this works in practice let’s visualize how to transform the GraphQL types into TypeScript types. First let’s take a look at this graph
We will first define our GraphQL schema on our server. Then we need to generate static typings on the frontend to type the results and arguments for queries and mutations. We also need to generate separate static typings on the backend for our resolvers. Every time our GraphQL schema changes we also need to update our affected static typings. The GraphQL gateway is now the single source of truth for typings, but in order to remove the friction between definitions we need to introduce automation. This way we will not have to keep everything in sync manually.
Generating types on the frontend with GraphQL CodeGen
Let’s generate TypeScript types for our responses from the GraphQL server. We will use a library called GraphQL CodeGen.
We will use our example repository. In order to execute the code you can clone the repository with
install dependencies with
and start the server in development with
GraphQL CodeGen yaml file
GraphQLCodeGen works on modular bases. There is a lot of plug-ins that enables you apply GraphQL CodeGen library to many different applications. For now we will use just two plug-ins
- TypeScript operations plugin: enables to generate types for mutations and queries
- TypeScript plugin: generate basic types from the schema
We can see that we need to first define a way how to retrieve the information about the schema. This is done in the schema field and in our case we used typeDef file, where schema in SDL is written. GraphQLCodeGen will then apply schema introspection and uses the results to generate TypeScript types. If your GraphQL server is running on port 3000, you can also perform introspection directly on the endpoint. Please note that for security purposes you should disable introspection in production; it should therefore only work in a development environment. We have also defined our path to GraphQL documents. In the example repository we store our GraphQL queries and mutations at our React component and the pattern above will validate all of them against our GraphQL schema and then generate TypeScript types for frontend. The last lines in the our GraphQLCodeGen config defines output path of the generated types and plug-ins used.
If you have installed graphql-codegen globally and you are in the folder of our example repository you can just execute:
otherwise you can use our npm script command:
This command will run a schema introspection query, take every *.graphql file that matches the specified pattern and validate it with our GraphQL schema. Based on each GraphQL file we will generate a new TypeScript types.
TypeScript output and how to use it in your React components
GraphQLCodeGen generated .ts, .d.ts files with types for each *.graphql requests into generated folder and we are able to import them into our React-Apollo components. Please note that for the sake of simplicity we did not implement React components in the repository. If you would like to generate Flow types or other supported types you can only change --target parameter. The following TypeScript file for the getUsers query should now be available in the queries/generated
I believe that the best way to operate is to generate type definitions every time you change your GraphQL schema. This will make your types up-to-date and you will avoid mismatches on your frontend. Now let's use our generated types for our React components in the repository. In our project we have one query for fetching subscriptions
On the client we are rendering our results in the table with two columns email and source. We use Apollo client and React Hooks for our data fetching. React component is written as follows:
Apollo client is written in TypeScript so it has good support for handling your types. We are passing our generated types in the useQuery hook. Our second GraphQL operation is subscribe mutation. Our component is written as follows:
In this case we used useMutation hook and again passed our generated types into useMutation function. These steps enabled us to use generated types on the client and each time we will change our GraphQL schema we will get up to date TypeScript suggestions.
Generating typed-safe resolvers on your server with GraphQLCodeGen
In order to generate server-side typings for your resolvers we need to use additional plugin. After updating our codegen.yaml we will get the following:
We can generate our types again with:
Now we have generated also types for our resolvers to server/generated/resolver-types.ts. We can now type all of our resolvers as follows:
How to take it even further?
But what about not just generating static types? What about generating your own code? This is something the GraphQLCodeGen library can also accomplish with plug-ins. For our project the most relevant plugin is for React Apollo. This can help you to skip one additional manual step of creating React Apollo components for mutations and queries.
I believe that automatic type and code generation is one of the biggest trends in GraphQL ecosystem. We are having great ecosystem for development especially for TypeScript and GraphQLCodeGen. You can use our starter project to speed up your set-up. This will help you diminish unnecessary friction between your static typing on the frontend with the GraphQL API. You can inject the command to regenerate types after each change in your GraphQL schema files. This way you will have your types automatically in sync with your API. A further advantage is that no extra communication between backend and frontend team members is required, since frontend engineers are notified about the changes in their types. We are additionally able to validate your queries and mutations in CI to avoid deploying queries and mutations on the frontend that do not comply to the current GraphQL schema. There is definitely space for improvement in libraries, especially for server side typings, but current implementations using GraphQLCodeGen is a promising step for more efficient work-flows. I believe that automatic type generation of static types using GraphQL not only in TypeScript has a bright future. It will allow us to spend less time writing boilerplate code and updating our types and more time on shipping high-quality typed products.