DEV Community

loading...
Cover image for How TypeScript can help you test your GraphQL Schema

How TypeScript can help you test your GraphQL Schema

shailennaidoo profile image Shailen Naidoo ・5 min read

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)

pic
Editor guide
Collapse
aexol profile image
Artur Czemiel

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?

Collapse
shailennaidoo profile image
Shailen Naidoo Author

That's pretty cool, I'll check it out for