Better Type Safety for your GraphQL resolvers with GraphQL Codegen
If you use TypeScript to write your GraphQL schema implementation, you’ll love the integration with
GraphQL Codegen and typescript-resolvers
plugin.
This plugin allows you to easily have typings for your resolvers, with super flexible configuration that allow you to integrate it easily to your existing code, types and models.
That means that you can type-seal your code and have complete type-safety: your GraphQL resolvers will be typed (parent type, args, inputs, return value, context type), and you can use your own TypeScript type models, so you can have type-safety all across your implementation, from API to database.
Having type check on your resolvers can help to improve your code quality, detect issues in build time (instead of runtime), and improve developer experience.
Getting Started
If you are already familiar with GraphQL Code Generator, you can skip this step.
Start by installing GraphQL Codegen and the relevant plugins:
yarn add @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers
Now, create codegen.yml
file with the following, make sure to point to your schema location:
schema: YOUR_SCHEMA_LOCATION_HERE
generates:
./resolvers-types.ts:
plugins:
- typescript
- typescript-resolvers
GraphQL Code Generator uses
graphql-tools
so you can point to your schema files, or/graphql
endpoint.
To run GraphQL Codegen, use: yarn graphql-codegen
(or, create a script for it if you wish). This
should create a new file called resolvers-types.ts
in your codebase.
Simple Resolvers Signature
In this example, we’ll use the following GraphQL schema and resolvers as reference. You can find a working live-demo of this part here.
This is a naive implementation of a GraphQL API, and we’ll see how more advanced use cases could be implemented in the upcoming steps.
To get started with the generated files, import Resolvers
identifier from your generated file, and
use it to type your resolvers object, for example:
Now, TypeScript engine will verify that object you returned, and you’ll be able to see that if you’ll change one of the fields, it will be type checked immediately:
Also, if you’ll change your schema types and re-run the codegen (or use Watch Mode), it will re-generate the matching types and check your code again.
As your probably understood, the default behavior of typescript-resolvers
is using the base type
generated by typescript
, that means, that your schema types and resolvers needs to match and have
the same signature and structure.
But it’s not always the case - because your GraphQL schema, in most cases, isn’t the same as your
models types - this is why we have mappers
configuration.
Use Your Model Types
Models types are the way your data is being stored or represented behind the scenes. Think about
User
object from example - in most cases, the representation of User
in your database (or any
other downstream API) is different than the way your represent User
in your API. Sometimes it’s
because of security considerations, and sometimes because fields are internal and used only by you,
and not by the consumers.
Those model types are the actual objects that you are usually using in your resolvers code. Those
can be created manually (with a simple TypeScript type
, interface
or class
), or created
automatically from your downstream APIs, database or any other data-source that you use in your app.
The way to tell codegen where are your models types are located is called mappers
.
To use mappers
configuration, we need first to setup a real type safety, and have models types for
our actual objects.
Let’s assumes that your backend is implemented this way, with some models types:
It means that now, your resolvers implementation needs to adjusted and handle the different data structure:
Noticed the errors? it caused by the fact that we don’t have the appropriate mapping set yet. We need to tell GraphQL codegen that our schema types are different than the model types.
To do that, let’s update codegen config with mappers
and add a mapping between a GraphQL type to a
TypeScript type (and the file it’s located in):
This way, GraphQL Codegen will use your custom models types in the generated output, instead of the default types, and your resolvers’ implementation will look like that:
Note that now you’ll get autocomplete, type safety and a better connection between your GraphQL schema and your GraphQL resolvers:
Typed Context
typescript-resolvers
also supports replacing the context
type of your resolvers’ implementation.
All you have to do, is to add this following to your codegen configuration:
schema: schema.graphql
generates:
./resolvers-types.ts:
config:
contextType: models#MyContextType
mappers:
User: ./models#UserModel
Profile: ./models#UserProfile
plugins:
- typescript
- typescript-resolvers
This will make sure to replace any
with MyContextType
, and you’ll be able to access a
fully-typed context
object in your resolvers.
What’s Next?
A few notes that worth mentioning:
- You can use
mappers
on every GraphQL type, interface or a union. - Your resolvers’ arguments (
args
) are also fully-typed, according to your schema definition. - The
parent
value is also fully typed, based on yourmappers
. - You can import your types from a node module package (
User: models-lib#UserType
). - You can also map to built-in language types (
DateType: Date
) - Aliasing the imports is also possible (
User: ./models#User as MyCustomUserType
)
You can also modify the default mapper (defaultMapper
) and allow partial resolution, this will
allow you to return partial objects in every resolver
(more info):
config:
useIndexSignature: true
defaultMapper: Partial<{T}>
For more advanced use-cases, you can find the complete plugin documentation here.
Join our newsletter
Want to hear from us when there's something new?
Sign up and stay up to date!
*By subscribing, you agree with Beehiiv’s Terms of Service and Privacy Policy.