Skip to content

docs: type generation for graphql servers #4376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: 16.x.x
Choose a base branch
from

Conversation

sarahxsanders
Copy link
Contributor

Adds new guide: Type Generation for GraphQL Servers

This is a part of the effort to expand GraphQL.js documentation

@sarahxsanders sarahxsanders requested a review from a team as a code owner April 22, 2025 23:19
@sarahxsanders sarahxsanders changed the title docs: add guide for graphQL server type generation docs: type generation for graphql servers Apr 22, 2025
Copy link

Hi @sarahxsanders, I'm @github-actions bot happy to help you with this PR 👋

Supported commands

Please post this commands in separate comments and only one per comment:

  • @github-actions run-benchmark - Run benchmark comparing base and merge commits for this PR
  • @github-actions publish-pr-on-npm - Build package from this PR and publish it on NPM

Copy link

@eddeee888 eddeee888 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @sarahxsanders !
I'm from the GraphQL Codegen team at The Guild. I've left some minor comments about our recommendation when using Codegen. Keen to hear your thoughts!

@JoviDeCroock
Copy link
Member

I've fixed CI after rebasing this branch it will work again, apologies for the issues

@eddeee888
Copy link

Hi @sarahxsanders , thanks for making the changes to recommend presets.
Since preset config and benefits are very different from the base plugins, I wonder if there's a chance I could write those sections? Happy to provide direction too, but it's going to be very different from what we currently have 🙂

@sarahxsanders
Copy link
Contributor Author

@eddeee888 you are absolutely welcome to write those sections! happy to collab if you want a review or anything

@eddeee888
Copy link

Thank you @sarahxsanders ! I'll draft something up this week, and let you know

Comment on lines +75 to +197
},
};
export default config;
```

To run the generator:

```bash
npx graphql-codegen
```

This creates a set of resolver types like:

```ts
export type QueryResolvers<ContextType = any> = {
user?: Resolver<User, any, ContextType, RequireFields<QueryUserArgs, 'id'>>;
};
```

These types ensure that the `user` resolver expects an `id` argument and returns a
`User`, giving you confidence and autocomplete while implementing your server logic.

## Using generated types in your server

Once generated, you can use these types directly in your resolver map:

```ts
import { QueryResolvers } from './generated/resolvers-types';

export const queryResolvers: QueryResolvers = {
user: (parent, args, context) => {
return context.db.getUser(args.id);
},
};
```

You can also extract shared `ContextType` and `Resolver`
utility types from the generated file and apply them across your codebase.

## Generating operation types

In addition to resolver types, you can generate types for GraphQL operations such
as queries, mutations, and fragments. This is especially useful for shared integration tests
or client logic that needs to match the schema precisely.

We recommend using the GraphQL Code Generator Server Preset for generating operation types.
The server preset generates both resolver and operation types, without needing to install
or configure additional plugins.

Suppose you have a query in `./src/operations/getUser.graphql`:

```graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
```

Update your codegen config:

```ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: './schema.graphql',
documents: './src/operations/**/*.graphql',
generates: {
'./src/generated/': {
preset: 'graphql-codegen-preset-server',
},
},
};
export default config;
```

This produces types like `GetUserQuery` and `GetUserQueryVariables`, which you can
import into your client code or test files.

## Typing resolvers manually

If you aren't ready to introduce type generation, you can still get partial type safety
using `graphql-js` built-in types.

```ts
import { GraphQLFieldResolver } from 'graphql';

const myResolver: GraphQLFieldResolver<MyParent, MyContext> = (
parent,
args,
context,
info
) => {
// ...
};
```

This pattern may be enough for small projects or static schemas, but it
can be hard to maintain and scale without automation.
Copy link

@eddeee888 eddeee888 May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
With a schema-first workflow, you can:
- Generate resolver type definitions that match your schema
- Generate operation types for client queries, integration tests, or internal tooling
- Detect breaking changes and unused types through schema diffing tools
## Generating resolver types
We recommend using the [GraphQL Code Generator Server Preset](https://the-guild.dev/graphql/codegen/docs/guides/graphql-server-apollo-yoga-with-server-preset)
to generate resolver types. It automatically generates resolver types based on your schema, including parent types,
arguments, return values, and context, without needing extra plugin setup.
Example `codegen.ts` config:
```ts
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: './schema.graphql',
generates: {
'./src/generated/resolvers-types.ts': {
plugins: ['typescript', 'typescript-resolvers'],
},
},
};
export default config;
```
To run the generator:
```bash
npx graphql-codegen
```
This creates a set of resolver types like:
```ts
export type QueryResolvers<ContextType = any> = {
user?: Resolver<User, any, ContextType, RequireFields<QueryUserArgs, 'id'>>;
};
```
These types ensure that the `user` resolver expects an `id` argument and returns a
`User`, giving you confidence and autocomplete while implementing your server logic.
## Using generated types in your server
Once generated, you can use these types directly in your resolver map:
```ts
import { QueryResolvers } from './generated/resolvers-types';
export const queryResolvers: QueryResolvers = {
user: (parent, args, context) => {
return context.db.getUser(args.id);
},
};
```
You can also extract shared `ContextType` and `Resolver`
utility types from the generated file and apply them across your codebase.
## Generating operation types
In addition to resolver types, you can generate types for GraphQL operations such
as queries, mutations, and fragments. This is especially useful for shared integration tests
or client logic that needs to match the schema precisely.
We recommend using the GraphQL Code Generator Server Preset for generating operation types.
The server preset generates both resolver and operation types, without needing to install
or configure additional plugins.
Suppose you have a query in `./src/operations/getUser.graphql`:
```graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
```
Update your codegen config:
```ts
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: './schema.graphql',
documents: './src/operations/**/*.graphql',
generates: {
'./src/generated/': {
preset: 'graphql-codegen-preset-server',
},
},
};
export default config;
```
This produces types like `GetUserQuery` and `GetUserQueryVariables`, which you can
import into your client code or test files.
## Typing resolvers manually
If you aren't ready to introduce type generation, you can still get partial type safety
using `graphql-js` built-in types.
```ts
import { GraphQLFieldResolver } from 'graphql';
const myResolver: GraphQLFieldResolver<MyParent, MyContext> = (
parent,
args,
context,
info
) => {
// ...
};
```
This pattern may be enough for small projects or static schemas, but it
can be hard to maintain and scale without automation.
With a schema-first workflow, you can:
- Generate resolver type definitions and files that match your schema
- Generate operation types for client queries, integration tests, or internal tooling
- Detect breaking changes and unused types through schema diffing tools
## Generating resolver types
We recommend using the [Server Preset](https://www.npmjs.com/package/@eddeee888/gcg-typescript-resolver-files) for a managed workflow. It automatically generates types and files based on your schema without needing extra plugin setup:
- resolver types, including parent types, arguments, return values, and context.
- resolver files with types wired up, ready for your business logic
- resolver map and type definitions to plug into GraphQL servers
Example codegen config:
```ts filename="codegen.ts"
import type { CodegenConfig } from "@graphql-codegen/cli";
import { defineConfig } from "@eddeee888/gcg-typescript-resolver-files";
const config: CodegenConfig = {
schema: "src/**/schema.graphql",
generates: {
"src/schema": defineConfig({
resolverGeneration: "minimal",
}),
},
};
export default config;

Note: this setup expects your schema is split into modules to improve readability and maintainability:

├── src/
│   ├── schema/
│   │   ├── base/
│   │   │   ├── schema.graphql
│   │   ├── user/
│   │   │   ├── schema.graphql
│   │   ├── book/
│   │   │   ├── schema.graphql

To run the generator:

npx graphql-codegen

This creates resolver files that like:

import type { QueryResolvers } from "./../../../types.generated";
export const user: NonNullable<QueryResolvers["user"]> = async (
  _parent,
  _arg,
  _ctx,
) => {
  /* Implement Query.user resolver logic here */
};

The user query resolver is typed to ensure that the user resolver expects an id argument and returns a
User, giving you confidence and autocomplete while implementing your server logic, which may look like this:

export const user: NonNullable<QueryResolvers["user"]> = async (
  parent,
  args,
  context,
) => {
  return context.db.getUser(args.id);
};

Check out the official Server Preset guide to learn about its other features, including mappers convention and static analysis for runtime safety.

Generating operation types

In addition to resolver types, you can generate types for GraphQL operations such as queries, mutations, and fragments. This is especially useful for shared integration tests or client logic that needs to match the schema precisely.

We recommend using the GraphQL Code Generator Client Preset for a managed workflow:

  • write operations with GraphQL syntax in the same file where it is used
  • get type-safety when using the result

Example codegen config

import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  schema: "src/**/schema.graphql",
  documents: ["src/**/*.ts"],
  ignoreNoDocuments: true,
  generates: {
    "./src/graphql/": {
      preset: "client",
      config: {
        documentMode: "string",
      },
    },
  },
};

export default config;

Now, run codegen in watch mode:

npx graphql-codegen --config codegen.ts --watch

Now, we can import the graphql function from src/graphql/ to write GraphQL operations directly in your TypeScript files:

import { graphql } from "./graphql";

const UserQuery = graphql(`
  query User($id: ID!) {
    user(id: ID!) {
      id
      fullName
    }
  }
`);

const response = await fetch("https://graphql.org/graphql/", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Accept: "application/graphql-response+json",
  },
  body: JSON.stringify({
    query: UserQuery,
    variables: { id: "1" },
  }),
});

if (!response.ok) {
  throw new Error("Network response was not ok");
}

const result: ResultOf<typeof UserQuery> = await response.json();

console.log(result);

Check out Client Preset guides for popular framework and GraphQL clients:

@eddeee888
Copy link

eddeee888 commented May 15, 2025

Hi @sarahxsanders, I've left a draft here: https://github.com/graphql/graphql-js/pull/4376/files#r2091097002
Please let me know what you think!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants