diff --git a/website/pages/docs/_meta.ts b/website/pages/docs/_meta.ts index 39ac3a1486..14f720b553 100644 --- a/website/pages/docs/_meta.ts +++ b/website/pages/docs/_meta.ts @@ -19,7 +19,12 @@ const meta = { 'constructing-types': '', 'oneof-input-objects': '', 'defer-stream': '', + 'type-generation': '', '-- 3': { + type: 'separator', + title: 'Testing', + }, + '-- 4': { type: 'separator', title: 'FAQ', }, diff --git a/website/pages/docs/type-generation.mdx b/website/pages/docs/type-generation.mdx new file mode 100644 index 0000000000..2170d60fb8 --- /dev/null +++ b/website/pages/docs/type-generation.mdx @@ -0,0 +1,208 @@ +--- +title: Type Generation for GraphQL +sidebarTitle: Type Generation +--- + +# Type Generation for GraphQL + +Writing a GraphQL server in JavaScript or TypeScript often involves managing complex +types. As your API grows, keeping these types accurate and aligned with your schema +becomes increasingly difficult. + +Type generation tools automate this process. Instead of manually defining or maintaining +TypeScript types for your schema and operations, these tools can generate them for you. +This improves safety, reduces bugs, and makes development easier to scale. + +This guide walks through common type generation workflows for projects using +`graphql-js`, including when and how to use them effectively. + +## Why use type generation? + +Type generation improves reliability and developer experience across the development +lifecycle. It's especially valuable when: + +- You want strong type safety across your server logic +- Your schema is defined separately in SDL files +- Your API surface is large, rapidly evolving, or used by multiple teams +- You rely on TypeScript for editor tooling, autocomplete, or static analysis + +By generating types directly from your schema, you can avoid drift between schema +definitions and implementation logic. + +## Code-first development + +In a code-first workflow, the schema is constructed entirely in JavaScript or TypeScript +using `graphql-js` constructors like `GraphQLObjectType`, `GraphQLSchema`, and others. +This approach is flexible and lets you build your schema programmatically using native +language features. + +If you're using this approach with TypeScript, you already get some built-in type safety +with the types exposed by `graphql-js`. For example, TypeScript can help ensure your resolver +functions return values that match their expected shapes. + +However, code-first development has tradeoffs: + +- You won't get automatic type definitions for your resolvers unless you generate +them manually or infer them through wrappers. +- Schema documentation, testing, and tool compatibility may require you to export +the schema to SDL first. + +You can still use type generation tools like GraphQL Code Generator in a code-first setup. +You just need to convert your schema into SDL. + +To export your schema: + +```ts +import { printSchema } from 'graphql'; +import { schema } from './schema'; +import { writeFileSync } from 'fs'; + +writeFileSync('./schema.graphql', printSchema(schema)); +``` + +Once you've written the SDL, you can treat the project like a schema-first project +for type generation. + +## Schema-first development + +In a schema-first workflow, your GraphQL schema is written in SDL, for example, `.graphql` +or `.gql` files. This serves as the source of truth for your server. This approach +emphasizes clarity because your schema is defined independently from your business logic. + +Schema-first development pairs well with type generation because the schema is +serializable and can be directly used by tools like [GraphQL Code Generator](https://the-guild.dev/graphql/codegen). + +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 = { + user?: Resolver>; +}; +``` + +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 = ( + 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. + +## Best practices for CI and maintenance + +To keep your type generation reliable and consistent: + +- Check in generated files to version control so teammates and CI systems don't produce +divergent results. +- Run type generation in CI to ensure types stay in sync with schema changes. +- Use schema diffing tools like `graphql-inspector` to catch breaking changes before +they're merged. +- Automate regeneration with pre-commit hooks, GitHub Actions, or lint-staged workflows. \ No newline at end of file