(This article is also available on graphqlmastery.com without the paywall.)
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 perform this operation with our example repository. In order to execute the code you can clone the repository with
git clone https://github.com/a7v8x/express-graphql-demo.git -b feature/6-static-typing
Then install the repository with npm
or with yarn
and launch the GraphQL server with the following commands
yarn run dev
In the repository we use the following schema
We would like to fetch the list of users. If you run your development server and you enter http://localhost:3000/graphql into the browser you will launch GraphiQL. We can then execute the following GraphQL document:
Now let’s imagine that you have React-Apollo frontend and you want to include TypeScript type definitions for the response of users query to use them as your prop types. 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 User type for example 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 active that will be typed as a Boolean. The updated User 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 tradeoff 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 just 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.
But how would we achieve that with GraphQL?
Visualization of our workflow
To get a better idea of how this works in practice let’s visualize how to transform the GraphQL schema into TypeScript or Flow typings. 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 Apollo CLI
Let’s generate TypeScript types for our responses from the GraphQL server. We will use a library called Apollo CLI. Please note, that Apollo tools introduced new version 2 with new API for executing commands. This article will be updated to this version in the following days, meanwhile you can tak a look at docs in their repository. In the example repository all the libraries here are installed as development dependencies and you can run commands with scripts defined in package.json, however, in the following paragraphs we will use the exact commands as it is more understandable for reader who do not want to clone the repository. Let’s start with installing Apollo CLI globally with
npm i -g email@example.com
or using yarn
yarn global add firstname.lastname@example.org
If your GraphQL server is still running on port 3000 in development, we can perform introspection of the schema with Apollo. Please note that for security purposes you should disable introspection in production; it should therefore only work in a development environment. In this tutorial we will save our introspection results in a schema.json file. Now let’s execute the following command
apollo schema:download --endpoint http://localhost:3000/graphql schema.json
This command will run a schema introspection query on your specified endpoint and save your schema as the JSON file in schema.json. We can then generate your typings. We will take every*.graphql file that matches the specified pattern and validate it with our schema.json file. Based on each GraphQL file we will generate a new TypeScript type. In our case we only have one query -getUsers. In order to generate it we need to execute the following command
apollo codegen:generate --schema schema.json --target typescript __generated__
Apollo then generates *.ts files with types for each *.graphql requests into __generated__ folders 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__
and also CreateUsers mutation type is available in the same folder
There is also additional file which is responsible for storing global typescript files again in __generated__ folder placed in the root of the project
I believe that the best way to operate is to generate type definitions every time you start your development server. The URL for the schema should be your staging server. Whenever you deploy a new version of your GraphQL schema on the backend you will fetch theschema.json again and regenerate your TypeScript typings. You will have static typings and *.graphql files close to your React/Angular components, and you will have everything structured by features with a unified folder structure for each feature. I believe that this is the best way to structure your project. Apollo CLI currently supports generating type definitions for TypeScript, Flow, Swift or Scala.
Generating typed-safe resolvers on your server with GraphQLCodeGen
As we have already mentioned, the type definitions generated from Apollo CLI are made by taking your *.graphql documents, validating them against your GraphQL schema stored in the JSON file, and then generating files with types definitions. Apollo CLI currently supports mainly generating type definitions based on your GraphQL documents. In order to generate server-side typings for your resolvers we need to use the library called GraphQLCodeGen . If we use this package we need to install two libraries: the GraphQLCodeGen itself and the corresponding custom template. The GraphQLCodeGen currently only supports TypeScript. We will use graphql-codegen-typescript-template. With yarn we are able to install both packages as follows
yarn add graphql-code-generator graphql-codegen-typescript-resolvers-template --dev
Now we should be able to execute the following command to generate our typings. Even though you can perform introspection with GraphQLCodeGen as well, we will again use our already saved schema.json file previously obtained by the apollo:download command.
gql-gen -s schema.json -t graphql-codegen-typescript-resolvers-template --out types/resolver-templates.gen.ts
These typings are then stored in resolver-types.gen.ts and look in our case as follows:
and we are now ready to import them in our code. The GraphQLCodeGen is not the only promising tool for generating typings for resolvers. Recently, Prisma introduced the tool called GraphQLGen. It is still in the early stages and right now also only supportsTypeScript, though support for Flow and Reason are coming soon. There are a number of other libraries to help you generate static typings, but I have found these libraries to be the most promising. You need to take your time and decide what library will be best for your needs.
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. You can also generate your React/Angular components, scaffold resolvers, and more via custom templates. One template that I would like to mention is the graphql-codegen-typescript-react-apollo-template, which you can use to generate React-Apollo components immediately with TypeScript typings included. We can try it again with similar command as above. We will only use the endpoint of our dev server right away
gql-gen --schema http://localhost:3000/graphql -t graphql-codegen-typescript-react-apollo-template --out ./types/react-apollo.ts
And we will receive the following code.
This can help you optimize your workflow even more. You can then use generated component in the same way as any other Apollo components. For more information you can checkout this article from the author of react-apollo-template.
From the shallow point of view it seems to me that GraphQLCodeGen has more broad application then apollo-tools and with its custom templates it is more scalable, but currently only support TypeScript. We need take our time and see what will be the winner in the long term.
You can take a look at the repository with the examples from this article. I will try to keep this article and repository up-to-date with current developments and update it regularly with my experiences. If you are interested in GraphQL you can also check out our upcoming free GraphQL Language course. I am happy to receive any feedback, questions, requests or tips by email at email@example.com.