DEV Community

Cover image for Better Type Safety for your GraphQL resolvers with GraphQL Codegen
TheGuildBot for The Guild

Posted on • Updated on • Originally published at the-guild.dev

Better Type Safety for your GraphQL resolvers with GraphQL Codegen

This article was published on Sunday, May 17, 2020 by Dotan Simha @ The Guild Blog

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 StartedIf 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
Enter fullscreen mode Exit fullscreen mode


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
Enter fullscreen mode Exit fullscreen mode

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 SignatureIn 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 TypesModels 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 Contexttypescript-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 your mappers.
  • 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): yaml config: useIndexSignature: true defaultMapper: Partial<{T}> For more advanced use-cases, you can find the complete plugin documentation here.

Top comments (0)