GraphQL

Code Generation

18 min Lesson 29 of 35

Code Generation

Automate the generation of TypeScript types, React hooks, and resolvers from your GraphQL schema and operations using GraphQL Code Generator.

GraphQL Code Generator

Install and configure GraphQL Code Generator:

# Install dependencies npm install -D @graphql-codegen/cli \ @graphql-codegen/typescript \ @graphql-codegen/typescript-operations \ @graphql-codegen/typescript-react-apollo # Initialize configuration npx graphql-codegen init # Or create codegen.yml manually
# codegen.yml schema: http://localhost:4000/graphql documents: 'src/**/*.graphql' generates: src/generated/graphql.ts: plugins: - typescript - typescript-operations - typescript-react-apollo config: withHooks: true withComponent: false withHOC: false

Generating TypeScript Types

Generate type-safe TypeScript definitions from your schema:

# schema.graphql type User { id: ID! name: String! email: String! posts: [Post!]! } type Post { id: ID! title: String! content: String! author: User! } type Query { user(id: ID!): User users: [User!]! } type Mutation { createUser(input: CreateUserInput!): User! } input CreateUserInput { name: String! email: String! }
# Run code generation npm run codegen # Generated types in src/generated/graphql.ts export type User = { __typename?: 'User'; id: Scalars['ID']; name: Scalars['String']; email: Scalars['String']; posts: Array<Post>; }; export type Post = { __typename?: 'Post'; id: Scalars['ID']; title: Scalars['String']; content: Scalars['String']; author: User; }; export type CreateUserInput = { name: Scalars['String']; email: Scalars['String']; };
Note: Generated types automatically include __typename fields for GraphQL type identification and caching.

Generating React Hooks

Create type-safe React hooks from your GraphQL operations:

# src/graphql/queries.graphql query GetUser($id: ID!) { user(id: $id) { id name email posts { id title } } } query GetUsers { users { id name email } }
# src/graphql/mutations.graphql mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email } }
// Generated hooks in src/generated/graphql.ts export function useGetUserQuery( baseOptions: Apollo.QueryHookOptions<GetUserQuery, GetUserQueryVariables> ) { return Apollo.useQuery<GetUserQuery, GetUserQueryVariables>( GetUserDocument, baseOptions ); } export function useCreateUserMutation( baseOptions?: Apollo.MutationHookOptions< CreateUserMutation, CreateUserMutationVariables > ) { return Apollo.useMutation<CreateUserMutation, CreateUserMutationVariables>( CreateUserDocument, baseOptions ); } // Usage in React component import { useGetUserQuery, useCreateUserMutation } from './generated/graphql'; function UserProfile({ userId }: { userId: string }) { const { data, loading, error } = useGetUserQuery({ variables: { id: userId }, }); const [createUser] = useCreateUserMutation(); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> <h1>{data?.user?.name}</h1> <p>{data?.user?.email}</p> </div> ); }
Tip: Use the --watch flag with codegen to automatically regenerate types when GraphQL files change: npm run codegen -- --watch

Generating Resolvers

Generate type-safe resolver signatures for your server:

# codegen.yml for server-side schema: ./schema.graphql generates: src/generated/resolvers.ts: plugins: - typescript - typescript-resolvers config: useIndexSignature: true contextType: ./context#Context mappers: User: ./models#UserModel Post: ./models#PostModel
// Generated resolver types import { GraphQLResolveInfo } from 'graphql'; import { UserModel, PostModel } from './models'; import { Context } from './context'; export type Resolvers<ContextType = Context> = { Query?: QueryResolvers<ContextType>; Mutation?: MutationResolvers<ContextType>; User?: UserResolvers<ContextType>; Post?: PostResolvers<ContextType>; }; export type QueryResolvers<ContextType = Context> = { user?: Resolver<Maybe<User>, {}, ContextType, RequireFields<QueryUserArgs, 'id'>>; users?: Resolver<Array<User>, {}, ContextType>; }; export type MutationResolvers<ContextType = Context> = { createUser?: Resolver<User, {}, ContextType, RequireFields<MutationCreateUserArgs, 'input'>>; }; // Use generated types in your resolvers import { Resolvers } from './generated/resolvers'; export const resolvers: Resolvers = { Query: { user: async (_, { id }, { db }) => { return db.user.findUnique({ where: { id } }); }, users: async (_, __, { db }) => { return db.user.findMany(); }, }, Mutation: { createUser: async (_, { input }, { db }) => { return db.user.create({ data: input }); }, }, };

Codegen Configuration

Advanced configuration options for different use cases:

# codegen.yml - Full configuration example schema: http://localhost:4000/graphql documents: - 'src/**/*.graphql' - 'src/**/*.tsx' generates: # Client-side types and hooks src/generated/client.ts: plugins: - typescript - typescript-operations - typescript-react-apollo config: withHooks: true withComponent: false withHOC: false skipTypename: false enumsAsTypes: true constEnums: true # Server-side resolver types src/generated/server.ts: plugins: - typescript - typescript-resolvers config: useIndexSignature: true contextType: ../context#Context defaultMapper: Partial<{T}> mappers: User: ../models#UserModel Post: ../models#PostModel # GraphQL schema as TypeScript src/generated/schema.ts: plugins: - typescript - typescript-graphql-files-modules # Introspection result src/generated/introspection.json: plugins: - introspection hooks: afterAllFileWrite: - prettier --write - eslint --fix

CI/CD Integration

Integrate code generation into your build pipeline:

{ "scripts": { "codegen": "graphql-codegen --config codegen.yml", "codegen:watch": "graphql-codegen --config codegen.yml --watch", "prebuild": "npm run codegen", "build": "tsc", "pretest": "npm run codegen", "test": "jest" } }
# .github/workflows/ci.yml name: CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Generate GraphQL types run: npm run codegen - name: Run type check run: npm run type-check - name: Run tests run: npm test - name: Build run: npm run build
Warning: Always commit generated files to version control or regenerate them in CI/CD. Document your choice in your project README.
Exercise:
  1. Set up GraphQL Code Generator with both client and server configurations
  2. Create GraphQL operations in .graphql files
  3. Generate TypeScript types, React hooks, and resolver types
  4. Build a React component using generated hooks
  5. Implement server resolvers using generated types
  6. Add codegen to your build pipeline and test it