One strength of GraphQL is that it has types. In an ideal world, the consumer of the GraphQL would receive the payload strongly typed. The reason that it is not the case is natural. The data that is moving from the server to the client who invoked the query receives a JSON format that does not have any notion of type. However, the client who call the GraphQL know exactly which fields he wants to consume and GraphQL maps each queried fields to a type.
Objectives
The idea of this article is to let the user who
Constraints
Many articles discuss about introspection and how types can be generated by having the consumer connect directly to GraphQL server to fetch the schema. I had the constraint that I could not do it for security reason, hence I had to expose the schema by the server into another way and have the consumer uses the generated merged schemas.
Server Steps
On the server side, one step is required: exposing the schemas into a unified schema (file) that can be shared. By generating the type every time the server is compiled, the schema remains in synchronization. Hence, we do not lose the freshness of being connected directly to GraphQL.
I created a script in the package.json that execute a TypeScript script.
"generateunifiedschema": "ts-node src/scripts/mergeAllGraphqlSchemas.ts",
The code in the TypeScript file is simple — really simple. Here is the whole code:
import fs from "fs"; import { fileLoader, mergeTypes } from "merge-graphql-schemas"; import path from "path"; const documentTypeDefs = fileLoader("src/**/*.graphql"); const mergedSchema = mergeTypes(documentTypeDefs); const typesPath = "output_types"; const schemaPath = path.join(typesPath, "allSchema.graphql"); fs.writeFileSync(schemaPath, mergedSchema);
It uses the library merge-graphql-schemas and search for all individual schema that is spread inside the project. The result goes outside in a folder. That’s it. In my case, that generated file is available internal at Netflix but not exposed outside. It means that every developers that consume the GraphQL service can download the schema, create query in their TypeScript code and use the unified schema to generate their code.
Client Steps
Each consumer will have to do few steps. First, they need to download the unified schemas. Second, they need to write their GraphQL query and use gql. The gql allows to have a library that will scan the TypeScript file for gql (GraphQL Query) and will generate the appropriate TypeScript definition for the response data.
Download Unified Schema
The download step is a matter of using curl. I create a simple bash file:
curl -X GET \ http://retracted/allSchema.graphql \ -H 'accept: text/plain, */*' \ --silent \ --output allSchema.graphql
Query the GraphQL
The next step is to write the client code to query the data. There is many ways to do it, I’ll demonstrate one but will not go in detail in this article.
async () => { try { const result: ApolloQueryResult<Proto.Query> = await this.client.query<Proto.Query>({ query: gql` query Proto { org(orgId: 0) { name } } ` }); if (result.data.org && result.data.org.name) { this.setState({ longName: result.data.org.name }); } } catch (error) { console.error(error.networkError.bodyText); } }
At that point,
Generate the TypeScript type
The generation is done by using a library named
"generatetypes": "gql-gen --config graphql/codegen.yml", "generatetypes:watch": "gql-gen --config graphql/codegen.yml --watch"
The codegen.yml has the schema and document configuration. At first, I was confusing about these two terms. The documentation is dry on the differentiation. The schema configuration is about where to get the GraphQL schema, this is the downloaded unified GraphQL schema from the server. The document is which TypeScript file to analyze (could be JavaScript file as well).
schema: "./graphql/**/*.graphql" documents: "./src/**/*.+(tsx|ts)" overwrite: true generates: ./src/autogenerated/octoolstypes.ts: config: {} plugins: - "typescript-common" - "typescript-client" require: ['ts-node/register']
It uses few plugins, hence you need to get the NPM packages accordingly.
"graphql-code-generator": "0.16.0", "graphql-codegen-add": "0.16.0", "graphql-codegen-typescript-client": "0.16.0", "graphql-codegen-typescript-common": "0.16.0", "graphql-codegen-typescript-server": "0.16.0"
The
Conclusion
Having the type generating automatically from the backend is fascinating and is efficient. If your business domain is rich in quantity of entities (interfaces) and if many of them have several fields and complex interconnection than having the type generated instead of typing it manually is a relief.
Related GraphQL Articles
- Getting Started with GraphQL for Netflix Open Connect
- Install Apollo Server to host a GraphQL service
- Apollo Server and Secured Playground
- GraphQL Context
- GraphQL Query with Argument
- Apollo GraphQL Resolvers and Data Source separation
- How to setup a TypeScript, NodeJS, Express Apollo Server to easy debugging with VsCode
- GraphQL Resolvers with Apollo
- Configuring Apollo Playground and API on two different URL
- How to automatically generate TypeScript for consumers of your GraphQL