Skip to content

Commit 0e2f685

Browse files
sarahxsanderseddeee888benjie
authored
docs: type generation for graphql servers (#4376)
Adds new guide: Type Generation for GraphQL Servers This is a part of the effort to expand GraphQL.js documentation --------- Co-authored-by: Eddy Nguyen <[email protected]> Co-authored-by: Benjie <[email protected]>
1 parent d3483ff commit 0e2f685

File tree

3 files changed

+270
-0
lines changed

3 files changed

+270
-0
lines changed

cspell.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ overrides:
2525
- swcrc
2626
- noreferrer
2727
- xlink
28+
- codegen
2829
- composability
2930
- deduplication
31+
- debuggable
3032
- subschema
3133
- subschemas
3234
- NATS

website/pages/docs/_meta.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const meta = {
2323
'oneof-input-objects': '',
2424
'defer-stream': '',
2525
subscriptions: '',
26+
'type-generation': '',
2627
'cursor-based-pagination': '',
2728
'custom-scalars': '',
2829
'advanced-custom-scalars': '',
@@ -32,6 +33,10 @@ const meta = {
3233
'graphql-errors': '',
3334
'using-directives': '',
3435
'-- 3': {
36+
type: 'separator',
37+
title: 'Testing',
38+
},
39+
'-- 4': {
3540
type: 'separator',
3641
title: 'FAQ',
3742
},
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
---
2+
title: Type Generation for GraphQL
3+
sidebarTitle: Type Generation
4+
---
5+
6+
# Type Generation for GraphQL
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 provide
48+
a description of the schema in 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 produce an SDL description of 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 an SDL-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` (discouraged) 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](https://the-guild.dev/graphql/codegen).
74+
75+
With a schema-first workflow, you can:
76+
77+
- Generate resolver type definitions and files 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+
To get started, install the required packages:
84+
85+
```bash
86+
npm install graphql @graphql-codegen/cli @eddeee888/gcg-typescript-resolver-files
87+
```
88+
89+
This scoped package is published by a community maintainer and is widely used for GraphQL server
90+
type generation.
91+
92+
We recommend using the [Server Preset](https://www.npmjs.com/package/@eddeee888/gcg-typescript-resolver-files) for a
93+
managed workflow. It automatically generates types and files based on your schema without needing extra plugin setup.
94+
95+
The Server Preset generates:
96+
97+
- Resolver types, including parent types, arguments, return values, and context
98+
- Resolver files with types wired up, ready for your business logic
99+
- A resolver map and type definitions to plug into GraphQL servers
100+
101+
This setup expects your schema is split into modules to improve readability and maintainability. For example:
102+
103+
```text
104+
├── src/
105+
│ ├── schema/
106+
│ │ ├── base/
107+
│ │ │ ├── schema.graphql
108+
│ │ ├── user/
109+
│ │ │ ├── schema.graphql
110+
│ │ ├── book/
111+
│ │ │ ├── schema.graphql
112+
```
113+
114+
Here's an example `codegen.ts` file using the Server Preset:
115+
116+
```ts filename="codegen.ts"
117+
import type { CodegenConfig } from "@graphql-codegen/cli";
118+
import { defineConfig } from "@eddeee888/gcg-typescript-resolver-files";
119+
120+
const config: CodegenConfig = {
121+
schema: "src/**/schema.graphql",
122+
generates: {
123+
"src/schema": defineConfig({
124+
resolverGeneration: "minimal",
125+
}),
126+
},
127+
};
128+
129+
export default config;
130+
```
131+
132+
To generate the resolver types and files, run:
133+
134+
```bash
135+
npx graphql-codegen
136+
```
137+
138+
This creates resolver files like:
139+
140+
```ts filename="src/schema/user/resolvers/Query/user.ts"
141+
import type { QueryResolvers } from "./../../../types.generated";
142+
143+
export const user: NonNullable<QueryResolvers["user"]> = async (
144+
_parent,
145+
_arg,
146+
_ctx,
147+
) => {
148+
// Implement Query.user resolver logic here
149+
};
150+
```
151+
152+
The user query resolver is typed to ensure that the user resolver expects an id argument and returns a
153+
User, giving you confidence and autocomplete while implementing your server logic, which may look like this:
154+
155+
```ts filename="src/schema/user/resolvers/Query/user.ts"
156+
export const user: NonNullable<QueryResolvers["user"]> = async (
157+
parent,
158+
args,
159+
context,
160+
) => {
161+
return context.db.getUser(args.id);
162+
};
163+
```
164+
165+
See the official [Server Preset guide](https://the-guild.dev/graphql/codegen/docs/guides/graphql-server-apollo-yoga-with-server-preset) to learn about its other features, including mappers convention and static analysis for runtime safety.
166+
167+
## Generating operation types
168+
169+
In addition to resolver types, you can generate types for GraphQL operations such as queries, mutations, and
170+
fragments. This is especially useful for shared integration tests or client logic that needs to match the schema
171+
precisely.
172+
173+
To get started, install the required packages:
174+
175+
```bash
176+
npm install graphql @graphql-codegen/cli
177+
```
178+
179+
We recommend using the GraphQL Code Generator [Client Preset](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client) for a managed workflow:
180+
181+
- Write operations with GraphQL syntax in the same file where it is used
182+
- Get type-safety when using the result
183+
184+
Here's an example configuration using the Client Preset:
185+
186+
```ts filename="codegen.ts"
187+
import type { CodegenConfig } from "@graphql-codegen/cli";
188+
189+
const config: CodegenConfig = {
190+
schema: "src/**/schema.graphql",
191+
documents: ["src/**/*.ts"],
192+
ignoreNoDocuments: true,
193+
generates: {
194+
"./src/graphql/": {
195+
preset: "client",
196+
config: {
197+
documentMode: "string",
198+
},
199+
},
200+
},
201+
};
202+
203+
export default config;
204+
```
205+
206+
To keep generated types up to date as you edit your code, run the generator in watch mode:
207+
208+
```bash
209+
npx graphql-codegen --config codegen.ts --watch
210+
```
211+
212+
Once generated, import the `graphql` function from `src/graphql/` to write GraphQL operations
213+
directly in your TypeScript files:
214+
215+
```ts filename="src/index.ts"
216+
import { graphql } from "./graphql";
217+
218+
const UserQuery = graphql(`
219+
query User($id: ID!) {
220+
user(id: ID!) {
221+
id
222+
fullName
223+
}
224+
}
225+
`);
226+
227+
const response = await fetch("https://graphql.org/graphql/", {
228+
method: "POST",
229+
headers: {
230+
"Content-Type": "application/json",
231+
Accept: "application/graphql-response+json",
232+
},
233+
body: JSON.stringify({
234+
query: UserQuery,
235+
variables: { id: "1" },
236+
}),
237+
});
238+
239+
if (!response.ok) {
240+
throw new Error("Network response was not ok");
241+
}
242+
243+
const result: ResultOf<typeof UserQuery> = await response.json();
244+
245+
console.log(result);
246+
```
247+
248+
For guides on using the Client Preset with popular frameworks and tools, see:
249+
250+
- [Vanilla TypeScript](https://the-guild.dev/graphql/codegen/docs/guides/vanilla-typescript)
251+
- [React Query](https://the-guild.dev/graphql/codegen/docs/guides/react-query)
252+
- [React / Vue](https://the-guild.dev/graphql/codegen/docs/guides/react-vue)
253+
254+
## Best practices for CI and maintenance
255+
256+
To keep your type generation reliable and consistent:
257+
258+
- Check in generated files to version control so teammates and CI systems don't produce
259+
divergent results.
260+
- Run type generation in CI to ensure types stay in sync with schema changes.
261+
- Use schema diffing tools like `graphql-inspector` to catch breaking changes before
262+
they're merged.
263+
- Automate regeneration with pre-commit hooks, GitHub Actions, or lint-staged workflows.

0 commit comments

Comments
 (0)