graphql-codegen

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GraphQL Code Generator Core Knowledge

GraphQL Code Generator 核心知识

Deep Knowledge: Use
mcp__documentation__fetch_docs
with technology:
graphql-codegen
for comprehensive documentation.
深度知识:调用
mcp__documentation__fetch_docs
并指定技术为
graphql-codegen
可获取完整文档。

Installation

安装

bash
npm install -D @graphql-codegen/cli @graphql-codegen/typescript \
  @graphql-codegen/typescript-operations @graphql-codegen/client-preset
bash
npm install -D @graphql-codegen/cli @graphql-codegen/typescript \
  @graphql-codegen/typescript-operations @graphql-codegen/client-preset

Basic Configuration

基础配置

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

const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.tsx', 'src/**/*.ts'],
  ignoreNoDocuments: true,
  generates: {
    './src/gql/': {
      preset: 'client',
      config: {
        documentMode: 'string',
      },
    },
  },
};

export default config;
typescript
// codegen.ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.tsx', 'src/**/*.ts'],
  ignoreNoDocuments: true,
  generates: {
    './src/gql/': {
      preset: 'client',
      config: {
        documentMode: 'string',
      },
    },
  },
};

export default config;

Run Generation

执行生成

bash
npx graphql-codegen
npx graphql-codegen --watch  # Watch mode
bash
npx graphql-codegen
npx graphql-codegen --watch  # 监听模式

Client Preset (Recommended)

客户端预设(推荐)

The client preset generates everything needed for type-safe GraphQL.
typescript
// codegen.ts
const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.tsx'],
  generates: {
    './src/gql/': {
      preset: 'client',
      plugins: [],
      presetConfig: {
        gqlTagName: 'gql',
        fragmentMasking: { unmaskFunctionName: 'getFragmentData' },
      },
    },
  },
};
客户端预设会生成实现类型安全GraphQL所需的全部内容。
typescript
// codegen.ts
const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.tsx'],
  generates: {
    './src/gql/': {
      preset: 'client',
      plugins: [],
      presetConfig: {
        gqlTagName: 'gql',
        fragmentMasking: { unmaskFunctionName: 'getFragmentData' },
      },
    },
  },
};

Usage

使用方式

typescript
import { gql } from '../gql';
import { useQuery } from '@apollo/client';

const GET_USER = gql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`);

function UserProfile({ id }: { id: string }) {
  const { data, loading } = useQuery(GET_USER, {
    variables: { id },
  });

  if (loading) return <Spinner />;
  // data.user is fully typed
  return <div>{data?.user?.name}</div>;
}

typescript
import { gql } from '../gql';
import { useQuery } from '@apollo/client';

const GET_USER = gql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`);

function UserProfile({ id }: { id: string }) {
  const { data, loading } = useQuery(GET_USER, {
    variables: { id },
  });

  if (loading) return <Spinner />;
  // data.user 是完全类型化的
  return <div>{data?.user?.name}</div>;
}

TanStack Query Integration

TanStack Query 集成

bash
npm install -D @graphql-codegen/typescript-react-query
typescript
// codegen.ts
const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.graphql'],
  generates: {
    './src/gql/index.ts': {
      plugins: [
        'typescript',
        'typescript-operations',
        'typescript-react-query',
      ],
      config: {
        fetcher: {
          func: './fetcher#fetcher',
          isReactHook: false,
        },
        reactQueryVersion: 5,
        addInfiniteQuery: true,
      },
    },
  },
};
bash
npm install -D @graphql-codegen/typescript-react-query
typescript
// codegen.ts
const config: CodegenConfig = {
  schema: 'http://localhost:4000/graphql',
  documents: ['src/**/*.graphql'],
  generates: {
    './src/gql/index.ts': {
      plugins: [
        'typescript',
        'typescript-operations',
        'typescript-react-query',
      ],
      config: {
        fetcher: {
          func: './fetcher#fetcher',
          isReactHook: false,
        },
        reactQueryVersion: 5,
        addInfiniteQuery: true,
      },
    },
  },
};

Fetcher

Fetcher 实现

typescript
// src/gql/fetcher.ts
export const fetcher = <TData, TVariables>(
  query: string,
  variables?: TVariables
): (() => Promise<TData>) => {
  return async () => {
    const response = await fetch('http://localhost:4000/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getToken()}`,
      },
      body: JSON.stringify({ query, variables }),
    });

    const json = await response.json();
    if (json.errors) {
      throw new Error(json.errors[0].message);
    }
    return json.data;
  };
};
typescript
// src/gql/fetcher.ts
export const fetcher = <TData, TVariables>(
  query: string,
  variables?: TVariables
): (() => Promise<TData>) => {
  return async () => {
    const response = await fetch('http://localhost:4000/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getToken()}`,
      },
      body: JSON.stringify({ query, variables }),
    });

    const json = await response.json();
    if (json.errors) {
      throw new Error(json.errors[0].message);
    }
    return json.data;
  };
};

Generated Hooks Usage

生成Hook的使用

typescript
import { useGetUserQuery, useCreateUserMutation } from './gql';

function UserProfile({ id }: { id: string }) {
  const { data, isLoading } = useGetUserQuery({ id });
  const createMutation = useCreateUserMutation();

  const handleCreate = () => {
    createMutation.mutate({
      input: { name: 'John', email: 'john@example.com' },
    });
  };

  if (isLoading) return <Spinner />;
  return <div>{data?.user?.name}</div>;
}

typescript
import { useGetUserQuery, useCreateUserMutation } from './gql';

function UserProfile({ id }: { id: string }) {
  const { data, isLoading } = useGetUserQuery({ id });
  const createMutation = useCreateUserMutation();

  const handleCreate = () => {
    createMutation.mutate({
      input: { name: 'John', email: 'john@example.com' },
    });
  };

  if (isLoading) return <Spinner />;
  return <div>{data?.user?.name}</div>;
}

Fragment Colocation

Fragment 共置

typescript
// components/UserAvatar.tsx
import { gql, FragmentType, getFragmentData } from '../gql';

export const USER_AVATAR_FRAGMENT = gql(`
  fragment UserAvatar on User {
    id
    name
    avatarUrl
  }
`);

interface Props {
  user: FragmentType<typeof USER_AVATAR_FRAGMENT>;
}

export function UserAvatar({ user }: Props) {
  const data = getFragmentData(USER_AVATAR_FRAGMENT, user);
  return <img src={data.avatarUrl} alt={data.name} />;
}

// Usage in parent query
const GET_USER = gql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      ...UserAvatar
    }
  }
`);

typescript
// components/UserAvatar.tsx
import { gql, FragmentType, getFragmentData } from '../gql';

export const USER_AVATAR_FRAGMENT = gql(`
  fragment UserAvatar on User {
    id
    name
    avatarUrl
  }
`);

interface Props {
  user: FragmentType<typeof USER_AVATAR_FRAGMENT>;
}

export function UserAvatar({ user }: Props) {
  const data = getFragmentData(USER_AVATAR_FRAGMENT, user);
  return <img src={data.avatarUrl} alt={data.name} />;
}

// 父查询中的使用
const GET_USER = gql(`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      ...UserAvatar
    }
  }
`);

Production Readiness

生产就绪

Schema Polling

Schema 拉取配置

typescript
// codegen.ts
const config: CodegenConfig = {
  schema: [
    {
      'http://localhost:4000/graphql': {
        headers: {
          Authorization: `Bearer ${process.env.GRAPHQL_TOKEN}`,
        },
      },
    },
  ],
  // ...
};
typescript
// codegen.ts
const config: CodegenConfig = {
  schema: [
    {
      'http://localhost:4000/graphql': {
        headers: {
          Authorization: `Bearer ${process.env.GRAPHQL_TOKEN}`,
        },
      },
    },
  ],
  // ...
};

Multiple Schemas

多Schema配置

typescript
const config: CodegenConfig = {
  generates: {
    './src/gql/user-api/': {
      schema: 'http://user-api:4000/graphql',
      documents: ['src/features/user/**/*.tsx'],
      preset: 'client',
    },
    './src/gql/product-api/': {
      schema: 'http://product-api:4001/graphql',
      documents: ['src/features/product/**/*.tsx'],
      preset: 'client',
    },
  },
};
typescript
const config: CodegenConfig = {
  generates: {
    './src/gql/user-api/': {
      schema: 'http://user-api:4000/graphql',
      documents: ['src/features/user/**/*.tsx'],
      preset: 'client',
    },
    './src/gql/product-api/': {
      schema: 'http://product-api:4001/graphql',
      documents: ['src/features/product/**/*.tsx'],
      preset: 'client',
    },
  },
};

CI/CD Integration

CI/CD 集成

yaml
undefined
yaml
undefined

.github/workflows/codegen.yml

.github/workflows/codegen.yml

name: GraphQL Codegen
on: push: paths: - 'src//*.graphql' - 'src//*.tsx'
jobs: generate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx graphql-codegen - name: Check for changes run: | if [[ -n $(git status --porcelain src/gql) ]]; then echo "Generated files changed" exit 1 fi
undefined
name: GraphQL Codegen
on: push: paths: - 'src//*.graphql' - 'src//*.tsx'
jobs: generate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx graphql-codegen - name: Check for changes run: | if [[ -n $(git status --porcelain src/gql) ]]; then echo "Generated files changed" exit 1 fi
undefined

Package Scripts

包脚本配置

json
{
  "scripts": {
    "codegen": "graphql-codegen",
    "codegen:watch": "graphql-codegen --watch"
  }
}
json
{
  "scripts": {
    "codegen": "graphql-codegen",
    "codegen:watch": "graphql-codegen --watch"
  }
}

Checklist

检查清单

  • Schema source configured (URL or local)
  • Documents path matches source files
  • Client preset for optimal output
  • Fetcher configured with auth
  • Fragment colocation pattern
  • Watch mode for development
  • CI validation of generated code
  • TypeScript strict mode compatible
  • 已配置Schema来源(URL或本地文件)
  • 文档路径匹配源文件
  • 使用客户端预设获得最优输出
  • 已配置带鉴权的fetcher
  • 采用Fragment共置模式
  • 开发环境开启监听模式
  • CI中校验生成的代码
  • 兼容TypeScript严格模式

When NOT to Use This Skill

何时不应使用本技能

  • REST API type generation (use
    openapi-codegen
    skill)
  • tRPC type-safe APIs (use
    trpc
    skill)
  • GraphQL schema design (use
    graphql
    skill)
  • Non-TypeScript projects
  • Simple GraphQL queries without type generation needs
  • REST API类型生成(使用
    openapi-codegen
    技能)
  • tRPC类型安全API(使用
    trpc
    技能)
  • GraphQL schema设计(使用
    graphql
    技能)
  • 非TypeScript项目
  • 不需要类型生成的简单GraphQL查询

Anti-Patterns

反模式

Anti-PatternWhy It's BadSolution
Committing generated files to gitMerge conflicts, outdated typesAdd to .gitignore, generate in CI
Not using client presetVerbose configurationUse client preset for modern setup
Ignoring schema changesType mismatches at runtimeRun codegen in watch mode during dev
Missing operationId or query namesPoor generated hook namesName all queries/mutations
Not using fragmentsCode duplicationUse fragments for reusable fields
Generating types onlyMissing runtime validationCombine with schema validation
Hardcoding schema URLEnvironment couplingUse env variables for schema source
Not versioning generator packagesInconsistent output across teamPin generator versions
反模式问题原因解决方案
将生成的文件提交到git出现合并冲突、类型过时添加到.gitignore,在CI中生成
不使用客户端预设配置冗余冗长现代项目使用客户端预设
忽略Schema变更运行时出现类型不匹配开发期间开启codegen监听模式
缺少operationId或查询名称生成的Hook名称可读性差为所有查询/突变命名
不使用fragments代码重复使用fragments复用字段定义
仅生成类型缺少运行时校验结合Schema校验使用
硬编码Schema URL环境耦合使用环境变量配置Schema来源
生成器包不锁定版本团队成员生成结果不一致锁定生成器包的版本

Quick Troubleshooting

快速故障排查

IssuePossible CauseSolution
Generation failsInvalid schema or documentsValidate schema, check GraphQL syntax
Type errors after generationSchema/code mismatchRegenerate types, check schema changes
Missing typesDocuments path not matching filesCheck documents glob pattern
Duplicate operation namesSame query name in multiple filesUse unique operation names
Fragment not foundFragment not in documentsInclude fragment file in documents
Hook not generatedNot using React Query pluginAdd typescript-react-query plugin
"Cannot find module './gql'"Generation didn't runRun
npm run codegen
Slow generationToo many documentsOptimize glob patterns, use ignoreNoDocuments
Type inference not workingWrong import pathImport from generated gql folder
问题可能原因解决方案
生成失败Schema或文档无效校验Schema,检查GraphQL语法
生成后出现类型错误Schema/代码不匹配重新生成类型,检查Schema变更
类型缺失文档路径不匹配文件检查文档的glob匹配规则
操作名称重复多个文件中存在相同的查询名称使用唯一的操作名称
找不到FragmentFragment不在文档路径中将Fragment文件包含到documents配置中
未生成Hook未使用React Query插件添加typescript-react-query插件
"Cannot find module './gql'"未执行生成操作运行
npm run codegen
生成速度慢文档数量过多优化glob匹配规则,使用ignoreNoDocuments配置
类型推断不生效导入路径错误从生成的gql文件夹导入

Reference Documentation

参考文档

  • Client Preset
  • React Query Plugin
  • Typed Document Node
  • 客户端预设
  • React Query 插件
  • 类型化文档节点