graphql-codegen
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGraphQL Code Generator Core Knowledge
GraphQL Code Generator 核心知识
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor comprehensive documentation.graphql-codegen
深度知识:调用并指定技术为mcp__documentation__fetch_docs可获取完整文档。graphql-codegen
Installation
安装
bash
npm install -D @graphql-codegen/cli @graphql-codegen/typescript \
@graphql-codegen/typescript-operations @graphql-codegen/client-presetbash
npm install -D @graphql-codegen/cli @graphql-codegen/typescript \
@graphql-codegen/typescript-operations @graphql-codegen/client-presetBasic 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 modebash
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-querytypescript
// 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-querytypescript
// 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
undefinedyaml
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
undefinedname: 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
undefinedPackage 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 skill)
openapi-codegen - tRPC type-safe APIs (use skill)
trpc - GraphQL schema design (use skill)
graphql - 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-Pattern | Why It's Bad | Solution |
|---|---|---|
| Committing generated files to git | Merge conflicts, outdated types | Add to .gitignore, generate in CI |
| Not using client preset | Verbose configuration | Use client preset for modern setup |
| Ignoring schema changes | Type mismatches at runtime | Run codegen in watch mode during dev |
| Missing operationId or query names | Poor generated hook names | Name all queries/mutations |
| Not using fragments | Code duplication | Use fragments for reusable fields |
| Generating types only | Missing runtime validation | Combine with schema validation |
| Hardcoding schema URL | Environment coupling | Use env variables for schema source |
| Not versioning generator packages | Inconsistent output across team | Pin generator versions |
| 反模式 | 问题原因 | 解决方案 |
|---|---|---|
| 将生成的文件提交到git | 出现合并冲突、类型过时 | 添加到.gitignore,在CI中生成 |
| 不使用客户端预设 | 配置冗余冗长 | 现代项目使用客户端预设 |
| 忽略Schema变更 | 运行时出现类型不匹配 | 开发期间开启codegen监听模式 |
| 缺少operationId或查询名称 | 生成的Hook名称可读性差 | 为所有查询/突变命名 |
| 不使用fragments | 代码重复 | 使用fragments复用字段定义 |
| 仅生成类型 | 缺少运行时校验 | 结合Schema校验使用 |
| 硬编码Schema URL | 环境耦合 | 使用环境变量配置Schema来源 |
| 生成器包不锁定版本 | 团队成员生成结果不一致 | 锁定生成器包的版本 |
Quick Troubleshooting
快速故障排查
| Issue | Possible Cause | Solution |
|---|---|---|
| Generation fails | Invalid schema or documents | Validate schema, check GraphQL syntax |
| Type errors after generation | Schema/code mismatch | Regenerate types, check schema changes |
| Missing types | Documents path not matching files | Check documents glob pattern |
| Duplicate operation names | Same query name in multiple files | Use unique operation names |
| Fragment not found | Fragment not in documents | Include fragment file in documents |
| Hook not generated | Not using React Query plugin | Add typescript-react-query plugin |
| "Cannot find module './gql'" | Generation didn't run | Run |
| Slow generation | Too many documents | Optimize glob patterns, use ignoreNoDocuments |
| Type inference not working | Wrong import path | Import from generated gql folder |
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 生成失败 | Schema或文档无效 | 校验Schema,检查GraphQL语法 |
| 生成后出现类型错误 | Schema/代码不匹配 | 重新生成类型,检查Schema变更 |
| 类型缺失 | 文档路径不匹配文件 | 检查文档的glob匹配规则 |
| 操作名称重复 | 多个文件中存在相同的查询名称 | 使用唯一的操作名称 |
| 找不到Fragment | Fragment不在文档路径中 | 将Fragment文件包含到documents配置中 |
| 未生成Hook | 未使用React Query插件 | 添加typescript-react-query插件 |
| "Cannot find module './gql'" | 未执行生成操作 | 运行 |
| 生成速度慢 | 文档数量过多 | 优化glob匹配规则,使用ignoreNoDocuments配置 |
| 类型推断不生效 | 导入路径错误 | 从生成的gql文件夹导入 |
Reference Documentation
参考文档
- Client Preset
- React Query Plugin
- Typed Document Node
- 客户端预设
- React Query 插件
- 类型化文档节点