Docs
Launch GraphOS Studio

Generating types from a GraphQL schema

How to ensure your resolvers are type safe


👋 If you haven't set up a TypeScript project using Apollo Server yet, follow our

guide before continuing.

uses a to clearly define the available data for each type and in a

. Type generation libraries can take advantage of the strongly-typed nature of a to automatically generate TypeScript types based on that schema.

You can use these generated TS types in your to type-check that your resolvers' return values match the field types dictated by your schema. Type checking your resolvers enables you to catch errors quickly and gives you the peace of mind that type safety ensures.

Setting up your project

We'll use the

library to generate types based on our GraphQL schema. There are
multiple ways
to provide a schema to GraphQL Code Generator. Below, we'll show the most common method, which requires our schema to be in a .graphql file.

If you haven't already, move your server's schema into a .graphql file, like so:

schema.graphql
type Query {
books: [Book]
}
type Book {
title: String
author: String
}
type AddBookMutationResponse {
code: String!
success: Boolean!
message: String!
book: Book
}
type Mutation {
addBook(title: String, author: String): AddBookMutationResponse
}

If you moved your schema into a .graphql file, update your imports to ensure you're still properly passing your schema to your server. In the file where you create your server, you can read in your schema using readFileSync from the fs package:

src/index.ts
// ...other imports
import { readFileSync } from 'fs';
// Note: this uses a path relative to the project's
// root directory, which is the current working directory
// if the server is executed using `npm run`.
const typeDefs = readFileSync('./schema.graphql', { encoding: 'utf-8' });
interface MyContext {
dataSources: {
books: Book[];
};
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});
// ... start our server

Restart your server to ensure it can find and use your schema and that everything works as expected. Next, we'll install the packages we need to generate types automatically based on our schema.

Installing and configuring dependencies

Run the following command to install the @graphql-codegen/cli, @graphql-codegen/typescript, and @graphql-codegen/typescript-resolvers packages into your project's dev dependencies:

npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers

For more information on each package above, check out the

docs.

Next, we'll set up a configuration file to tell GraphQL Code Generator where and how to generate types. You can do this by manually creating a codegen.yml file or by using the following command, which walks you through the process:

npx graphql-code-generator init

Below is an example of a codegen.yml file:

# This configuration file tells GraphQL Code Generator how
# to generate types based on our schema.
schema: "./schema.graphql"
generates:
# Specify where our generated types should live.
./src/__generated__/resolvers-types.ts:
plugins:
- "typescript"
- "typescript-resolvers"
config:
useIndexSignature: true
# More on this below!
contextType: "../index#MyContext"

for more information on the above configuration options.

Finally, we recommend adding helpful scripts to your package.json file to ensure your TS types are regularly generated:

{
// ...
"scripts": {
"generate": "graphql-codegen --config codegen.yml",
"compile": "npm run generate && tsc",
"start": "npm run compile && node ./dist/index.js",
},
// ...
}

We also recommend

, enabling your types to regenerate and your TypeScript files to recompile in the background as you work.

Above, running the npm start command generates types based on our GraphQL schema and compiles our TypeScript code. The first time you run the graphql-codegen command, you'll see a file full of generated types at the path you specified in your codegen.yml file.

Adding types to resolvers

The typescript-resolvers plugin creates a Resolvers type that you can use to add a type to your

, ensuring your resolvers return values match the field types specified by your schema.

Import the Resolvers type into the file where you define your resolvers:

resolvers.ts
// This is the file where our generated types live
// (specified in our `codegen.yml` file)
import { Resolvers } from './__generated__/resolvers-types';

You can now add the Resolvers type directly to your map:

export const resolvers: Resolvers = {}

Your resolvers can now type check that the arguments and return value for each resolver match the schema:

export const resolvers: Resolvers = {
Query: {
// TypeScript now complains about the below resolver because
// the data returned by this resolver doesn't match the schema type
// (i.e., type Query { books: [Book] })
books: () => {
return "apple";
},
},
}

If your resolvers are in multiple files, you can pull out the corresponding generated types for the resolvers into those files. For example, below, we import the generated types into the separate files we have for our queries and :

resolvers/queries.ts
import { QueryResolvers } from '__generated__/resolvers-types';
// Use the generated `QueryResolvers`
// type to type check our queries!
const queries: QueryResolvers = {
// ...queries
};
export default queries;
resolvers/mutations.ts
import { MutationResolvers } from '__generated__/resolvers-types';
// Use the generated `MutationResolvers` type
// to type check our mutations!
const mutations: MutationResolvers = {
// ...mutations
};
export default mutations;

Context typing for resolvers

You can also configure GraphQL Code Generator to add a type for the context your resolvers share, ensuring TypeScript warns you if you attempt to use a value that doesn't exist.

To do this, you must first export the interface you pass to as a generic type parameter for typing your context value:

src/index.ts
export interface MyContext {
dataSources: {
books: Book[];
};
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});

Remember the contextType from our

? You can pass your exported context interface to the contextType configuration option, like so:

# ...
config:
useIndexSignature: true
# Providing our context's interface ensures our
# context's type is set for all of our resolvers.
# Note, this file path starts from the location of the
# file where you generate types.
# (i.e., `/src/__generated__/resolvers-types.ts` above)
contextType: "../index#MyContext"

Once you regenerate your types, your context value is now automatically typed in all of your resolvers:

const resolvers: Resolvers = {
Query: {
// Our third argument (`contextValue`) has a type here, so we
// can check the properties within our resolver's shared context value.
books: (_, __, contextValue) => {
return contextValue.dataSources.books;
},
},
}

Basic runnable example

Check out our example using Apollo Server with generated types on CodeSandbox:

Edit server-generated-types-as4

See

for further guidance on the different features and integrations it supports.

Previous
Request format
Next
Mocking
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company