Skip to content

Commit b3b489f

Browse files
committed
add guide for graphQL server type generation
1 parent 5adeb7f commit b3b489f

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

website/pages/docs/_meta.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ const meta = {
1919
'constructing-types': '',
2020
'oneof-input-objects': '',
2121
'defer-stream': '',
22+
'type-generation': '',
2223
'-- 3': {
24+
type: 'separator',
25+
title: 'Testing',
26+
},
27+
'-- 4': {
2328
type: 'separator',
2429
title: 'FAQ',
2530
},
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
---
2+
title: Type Generation for GraphQL Servers
3+
sidebarTitle: Type Generation
4+
---
5+
6+
# Type Generation for GraphQL Servers
7+
8+
Writing a GraphQL server in JavaScript or TypeScript often involves managing complex
9+
types. As your API grows, keeping these types accurate and aligned with your schema
10+
becomes increasingly difficult.
11+
12+
Type generation tools automate this process. Instead of manually defining or maintaining
13+
TypeScript types for your schema and operations, these tools can generate them for you.
14+
This improves safety, reduces bugs, and makes development easier to scale.
15+
16+
This guide walks through common type generation workflows for projects using
17+
`graphql-js`, including when and how to use them effectively.
18+
19+
## Why use type generation?
20+
21+
Type generation improves reliability and developer experience across the development
22+
lifecycle. It's especially valuable when:
23+
24+
- You want strong type safety across your server logic
25+
- Your schema is defined separately in SDL files
26+
- Your API surface is large, rapidly evolving, or used by multiple teams
27+
- You rely on TypeScript for editor tooling, autocomplete, or static analysis
28+
29+
By generating types directly from your schema, you can avoid drift between schema
30+
definitions and implementation logic.
31+
32+
## Code-first development
33+
34+
In a code-first workflow, the schema is constructed entirely in JavaScript or TypeScript
35+
using `graphql-js` constructors like `GraphQLObjectType`, `GraphQLSchema`, and others.
36+
This approach is flexible and lets you build your schema programmatically using native
37+
language features.
38+
39+
If you're using this approach with TypeScript, you already get some built-in type safety
40+
with the types exposed by `graphql-js`. For example, TypeScript can help ensure your resolver
41+
functions return values that match their expected shapes.
42+
43+
However, code-first development has tradeoffs:
44+
45+
- You won't get automatic type definitions for your resolvers unless you generate
46+
them manually or infer them through wrappers.
47+
- Schema documentation, testing, and tool compatibility may require you to export
48+
the schema to SDL first.
49+
50+
You can still use type generation tools like GraphQL Code Generator in a code-first setup.
51+
You just need to convert your schema into SDL.
52+
53+
To export your schema:
54+
55+
```ts
56+
import { printSchema } from 'graphql';
57+
import { schema } from './schema';
58+
import { writeFileSync } from 'fs';
59+
60+
writeFileSync('./schema.graphql', printSchema(schema));
61+
```
62+
63+
Once you've written the SDL, you can treat the project like a schema-first project
64+
for type generation.
65+
66+
## Schema-first development
67+
68+
In a schema-first workflow, your GraphQL schema is written in SDL, for example, `.graphql`
69+
or `.gql` files. This serves as the source of truth for your server. This approach
70+
emphasizes clarity because your schema is defined independently from your business logic.
71+
72+
Schema-first development pairs well with type generation because the schema is
73+
serializable and can be directly used by tools like GraphQL Code Generator.
74+
75+
With a schema-first workflow, you can:
76+
77+
- Generate resolver type definitions that match your schema
78+
- Generate operation types for client queries, integration tests, or internal tooling
79+
- Detect breaking changes and unused types through schema diffing tools
80+
81+
## Generating resolver types
82+
83+
GraphQL Code Generator can generate resolver scaffolding based on your schema. These
84+
types help you implement resolvers with full type safety, including parent types,
85+
argument shapes, return values, and context.
86+
87+
Example `codegen.ts` config:
88+
89+
```ts
90+
import type { CodegenConfig } from '@graphql-codegen/cli';
91+
92+
const config: CodegenConfig = {
93+
schema: './schema.graphql',
94+
generates: {
95+
'./src/generated/resolvers-types.ts': {
96+
plugins: ['typescript', 'typescript-resolvers'],
97+
},
98+
},
99+
};
100+
export default config;
101+
```
102+
103+
To run the generator:
104+
105+
```bash
106+
npx graphql-codegen
107+
```
108+
109+
This creates a set of resolver types like:
110+
111+
```ts
112+
export type QueryResolvers<ContextType = any> = {
113+
user?: Resolver<User, any, ContextType, RequireFields<QueryUserArgs, 'id'>>;
114+
};
115+
```
116+
117+
These types ensure that the `user` resolver expects an `id` argument and returns a
118+
`User`, giving you confidence and autocomplete while implementing your server logic.
119+
120+
## Using generated types in your server
121+
122+
Once generated, you can use these types directly in your resolver map:
123+
124+
```ts
125+
import { QueryResolvers } from './generated/resolvers-types';
126+
127+
export const queryResolvers: QueryResolvers = {
128+
user: (parent, args, context) => {
129+
return context.db.getUser(args.id);
130+
},
131+
};
132+
```
133+
134+
You can also extract shared `ContextType` and `Resolver`
135+
utility types from the generated file and apply them across your codebase.
136+
137+
## Generating operation types
138+
139+
In addition to resolver types, you can generate types for GraphQL operations such
140+
as queries, mutations, and fragments. This is especially useful for shared integration tests
141+
or client logic that needs to match the schema precisely.
142+
143+
Suppose you have a query in `./src/operations/getUser.graphql`:
144+
145+
```graphql
146+
query GetUser($id: ID!) {
147+
user(id: $id) {
148+
id
149+
name
150+
}
151+
}
152+
```
153+
154+
Update your codegen config:
155+
156+
```ts
157+
const config = {
158+
schema: './schema.graphql',
159+
documents: './src/operations/**/*.graphql',
160+
generates: {
161+
'./src/generated/operations.ts': {
162+
plugins: ['typescript', 'typescript-operations'],
163+
},
164+
},
165+
};
166+
```
167+
168+
This produces types like `GetUserQuery` and `GetUserQueryVariables`, which you can
169+
import into your client code or test files.
170+
171+
## Typing resolvers manually
172+
173+
If you aren't ready to introduce type generation, you can still get partial type safety
174+
using `graphql-js` built-in types.
175+
176+
```ts
177+
import { GraphQLFieldResolver } from 'graphql';
178+
179+
const myResolver: GraphQLFieldResolver<MyParent, MyContext> = (
180+
parent,
181+
args,
182+
context,
183+
info
184+
) => {
185+
// ...
186+
};
187+
```
188+
189+
This pattern may be enough for small projects or static schemas, but it
190+
can be hard to maintain and scale without automation.
191+
192+
## Best practices for CI and maintenance
193+
194+
To keep your type generation reliable and consistent:
195+
196+
- Check in generated files to version control so teammates and CI systems don't produce
197+
divergent results.
198+
- Run type generation in CI to ensure types stay in sync with schema changes.
199+
- Use schema diffing tools like `graphql-inspector` to catch breaking changes before
200+
they're merged.
201+
- Automate regeneration with pre-commit hooks, GitHub Actions, or lint-staged workflows.

0 commit comments

Comments
 (0)