Better Type Safety for your GraphQL resolvers with GraphQL Codegen
Tags: graphql, codegen, node, typescript
Published: 2021-07-22 08:58:08

> This article was published on Sunday, May 17, 2020 by [Dotan Simha](https://twitter.com/dotansimha) @ [The Guild Blog](https://the-guild.dev/)

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](http://graphql-code-generator.com), you
can skip this step.Start by installing GraphQL Codegen and the relevant plugins:```sh
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:```yaml
schema: YOUR_SCHEMA_LOCATION_HERE
generates:
  ./resolvers-types.ts:
    plugins:
      - typescript
      - typescript-resolvers
```> GraphQL Code Generator uses [`graphql-tools`](http://graphql-tools.com) 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](https://codesandbox.io/s/condescending-albattani-ixyp9?file=/resolvers.ts).{% codesandbox condescending-albattani-ixyp9 module=schema.graphql %}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:{% codesandbox condescending-albattani-ixyp9 module=resolvers.ts %}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:<img width="100%" style="width:100%" src="https://thumbs.gfycat.com/AccurateUnfitConure-size_restricted.gif" />Also, if you'll change your schema types and re-run the codegen (or use
[Watch Mode](https://graphql-code-generator.com/docs/getting-started/development-workflow#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:{% codesandbox silly-kalam-ym77z module=models.ts %}It means that now, your resolvers implementation needs to adjusted and handle the different data
structure:{% codesandbox silly-kalam-ym77z module=resolvers-with-error.ts %}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):{% codesandbox silly-kalam-ym77z module=codegen-with-mappers.yml %}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:{% codesandbox silly-kalam-ym77z module=resolvers.ts %}Note that now you'll get autocomplete, type safety and a better connection between your GraphQL
schema and your GraphQL resolvers:<img width="100%" style="width:100%" src="https://thumbs.gfycat.com/PerfumedIllegalBee-size_restricted.gif" />## 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:```yaml
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](https://github.com/dotansimha/graphql-code-generator/issues/1133#issuecomment-456812621)):```yaml
config:
  useIndexSignature: true
  defaultMapper: Partial<{T}>
```For more advanced use-cases, you can find
[the complete plugin documentation here](https://graphql-code-generator.com/docs/plugins/typescript-resolvers/#configuration).