Just a few days ago, I realized something quite profound. I can actually use TypeScript as a layer between GraphQLâs type system and my Jest tests. Essentially the idea is that I can use TypeScriptâs powerful typing system to make sure that I am writing the correct types for my GraphQL schema.
âWhy would I need to write tests for my GraphQL schema?â
If you have ever worked on a large GraphQL project, you would soon realize that once the project gets big enough it gets hard to keep track of your types. It is very easy to lose track of what type goes where. Our schema is just a collection of strings, meaning that we donât get powerful autocompletion but if we interpolate TypeScript with our GraphQL schema we can achieve true type safety (in my opinion). For example:
Below we have a really simple schema using Apollo GraphQL
The above schema is pretty simple, we have one queryable field person
which has two fields name
and surname
There is one major problem and that is that our whole schema is contained within const Schema
, but we should be able to isolate the different types into their own constants such as:
The above example has the same outcome as the previous one but the only difference is that the types are isolated from one another, so essentially we are splitting our types into individual units. The question is: âHow do we ensure that are types are correctly typed? Can we write unit tests against our isolated types?â For example, I want to write a test to make sure that type Person.name
has the correct type of String!
and not String
, if we are able to achieve this level of testability with our schema, then we can rest assured that our GraphQL server is well written.
Here is a way you can do it with plain JavaScript using Jest:
In the above example, we are asserting type Query
and type Person
against the type we know (or think) it should be. The issue with the way we are testing here is that we are testing on a type
level, not on a field
level. Remember, we want to be able to assert that type Person.name
is actually a non-nullable string String!
. We could do this with JavaScript but it lacks the necessary type system to make this really powerful and intuitive. Now letâs get into how TypeScript can help us do this đ
Disclaimer! This is just my opinion on how to test GraphQL types, I might be completely wrong and if you find any errors in my method. Please share your ideasđ
TypeScriptâs real power
For a long time, I have been against TypeScript. Things have changed, I have finally seen the TypeScript light especially when it comes to server-side JavaScript. Now letâs get into how TypeScript + GraphQL + Jest = đ
Interop TypeScript with GraphQLâs type system
The first step is to define GraphQLâs scalar types using TypeScript, we can do this using enumâs. For example:
In the above example, we have defined GraphQLâs scalar types using TypeScriptâs enums. Now we are able to do this Types.IDNull === 'ID'
Create typed functions to generate GraphQL types
Now we can create functions that accept our enum Types
and return a well-typed type
. For example:
In the above example, we have a function createTypeQuery
that has arguments that relate to the fields of our type Query
. The arguments of the function are typed using interface TypeQueryFields
, the interface has a field _
which is assigned a type Types.BooleanNull
which is equal to 'Boolean'
. This string is what GraphQL knows to be a boolean according to its type system. So if we use the function like this:
import Types from './types';
createTypeQuery({ _: Types.StringNonNull });
TypeScript would throw a fit telling us something like:
Type 'Types.StringNonNull' is not assignable to type 'Types.BooleanNull'.ts(2322)
schema.ts(6, 3): The expected type comes from property '_' which is declared here on type 'TypeQueryFields'
The above error is perfect as I know that I am not generating the correct type Query
with the correct field types that I wanted. We are getting type errors for our types
not at runtime but at code-time đ
Letâs add another type
Now itâs time to get a little bit more complicated, letâs create type Person
using my method
In the above example, we have a much more complicated setup. Here we are defining everything about type Person
using TypeScriptâs type system, you might have also noticed extend type Query
which is just extending the original type Query
. Essentially we are scoping everything to person.ts that is the whole point.
You probably noticed this:
export enum Person {
Type = 'Person!';
}
interface TypeQueryPersonFields {
person: Person.Type;
}
The above enum Person
and interface TypeQueryPersonFields
is defining our custom type Person
as a usable type in TypeScript that we can pass to our function createTypeQueryPerson
, when we use the function to create the extend type
we can make sure that it is correct. For example:
If we had to do something like this:
import { Car } from './car';
const typeQueryPerson = createTypeQueryPerson({ person: Car.Type });
If we try to pass Car.Type
to createTypeQueryPerson
we would get a nice error from TypeScript đ
So you get the idea that we have some sort of type safety from TypeScript when trying to create our GraphQL types. Which is kinda what we need (or what I need), now how do we do tests on our types?
Time for the Jest-er
The best part about writing our GraphQL types like this is that we can now write some unit tests for them. Letâs check it out:
In the above example const realTypeQuery
is the result of createTypeQuery
this is our type that we want to test against. const testTypeQuery
is a result of:
const testTypeQuery = gql` type Query {
_: ${Types.BooleanNull}
} `;
If we change it to:
const typeQuery = gql` type Query {
_: ${Types.BooleanNonNull}
} `;
Our Jest tests would throw a nice error, telling us that our type that we are asserting to is incorrect. Which means we should probably make sure if our GraphQL type is the correct shape we want.
How I got this idea?
I was doing some freelance work at this non-profit called OpenUp, where they make extensive use of React, Styled-components, Material UI and a bunch of React based tools. Recently the lead frontend developer Schalk Venter decided to start using TypeScript as a part of their frontend tooling. Somehow seeing TypeScript being used with stateless-components gave me this idea to start encapsulating my GraphQL types and define a strict interface for them. This article is basically me showcasing my progress with the idea I got.
Conclusion
I strongly believe that if we have confidence in our GraphQL Schema through this sort of typing and tests we can be happy at the end of the day. Remember that our GraphQL server is nothing without the schema. The GraphQL type system is what makes it so powerful
If you have any questions you can contact me via Twitter: @shailen_naidoo
Thanks for checking out my article đ
Discussion (2)
Hey! Of course, you can use typescript to have typesafe queries. We created a typed queries generator which works online. For example, open up nightly.graphqleditor.com/showcase... and click the small arrow near the project name. Then click
Autocomplete .ts library
. What do you think?That's pretty cool, I'll check it out for