项目文件夹

文件
2026-07-13 21:36:20 +08:00

8.2 KiB

name, description, metadata
name description metadata
tooling 用于处理 GraphQL 操作的工具参考,包括代码生成与代码检查
type
reference

工具

本参考文档涵盖了用于处理 GraphQL 操作的工具,包括代码生成和代码检查。

目录

GraphQL 代码生成器

概述

GraphQL 代码生成器可从你的 schema 和操作中生成 TypeScript 类型,确保整个应用程序的类型安全。

安装

npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node

基本配置

创建 codegen.ts

// codegen.ts
import { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  overwrite: true,
  schema: "<URL_OF_YOUR_GRAPHQL_API>",
  // 假设所有源文件都在顶层的 `src/` 目录下——你可能需要根据你的文件结构调整此项
  documents: ["src/**/*.{ts,tsx}"],
  // 当没有文档时不以非零状态退出
  ignoreNoDocuments: true,
  generates: {
    // 使用最适合你应用程序结构的路径
    "./src/types/__generated__/graphql.ts": {
      plugins: ["typescript", "typescript-operations", "typed-document-node"],
      config: {
        avoidOptionals: {
          // 对可空字段使用 `null` 而非可选字段
          field: true,
          // 允许可空输入字段保持未指定状态
          inputValue: false,
        },
        // 对未配置的标量使用 `unknown` 而非 `any`
        defaultScalarType: "unknown",
        // Apollo Client 始终包含 `__typename` 字段
        nonOptionalTypename: true,
        // Apollo Client 不会为根类型添加 `__typename` 字段,因此
        // 不要为根操作类型生成 `__typename` 的类型。
        skipTypeNameForRoot: true,
      },
    },
  },
};

export default config;

运行生成

# 一次性生成
npx graphql-codegen

# 开发时的监听模式
npx graphql-codegen --watch

Package 脚本

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

生成类型的使用

// 之前:手动类型定义
const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

// 手动类型定义
interface GetUserData {
  user: {
    id: string;
    name: string;
  } | null;
}

const { data } = useQuery<GetUserData>(GET_USER, { variables: { id } });

// 之后:使用生成类型
import { useGetUserQuery } from "./generated/graphql";

const { data } = useGetUserQuery({ variables: { id } });
// data.user 现在是完整类型化的!

近操作文件生成

在操作文件旁边生成类型:

const config: CodegenConfig = {
  schema: "http://localhost:4000/graphql",
  documents: ["src/**/*.graphql"],
  generates: {
    "src/": {
      preset: "near-operation-file",
      presetConfig: {
        extension: ".generated.ts",
        baseTypesPath: "generated/graphql.ts",
      },
      plugins: ["typescript-operations", "typescript-react-apollo"],
    },
    "src/generated/graphql.ts": {
      plugins: ["typescript"],
    },
  },
};

结果如下:

src/
  components/
    UserCard/
      UserCard.graphql
      UserCard.generated.ts  # 该文件的生成类型

片段类型

// UserAvatar.graphql
// fragment UserAvatar on User {
//   id
//   name
//   avatarUrl
// }

import { UserAvatarFragment } from "./UserAvatar.generated";

interface UserAvatarProps {
  user: UserAvatarFragment;
}

export function UserAvatar({ user }: UserAvatarProps) {
  return <img src={user.avatarUrl} alt={user.name} />;
}

ESLint GraphQL

安装

npm install -D @graphql-eslint/eslint-plugin

配置

// eslint.config.js(扁平配置)
import graphqlPlugin from "@graphql-eslint/eslint-plugin";

export default [
  {
    files: ["**/*.graphql"],
    languageOptions: {
      parser: graphqlPlugin.parser,
    },
    plugins: {
      "@graphql-eslint": graphqlPlugin,
    },
    rules: {
      "@graphql-eslint/known-type-names": "error",
      "@graphql-eslint/no-anonymous-operations": "error",
      "@graphql-eslint/no-duplicate-fields": "error",
      "@graphql-eslint/naming-convention": [
        "error",
        {
          OperationDefinition: {
            style: "PascalCase",
            forbiddenPrefixes: ["Query", "Mutation", "Subscription"],
          },
          FragmentDefinition: {
            style: "PascalCase",
          },
        },
      ],
    },
  },
];

推荐规则

rules: {
  // 语法与合法性
  '@graphql-eslint/known-type-names': 'error',
  '@graphql-eslint/known-fragment-names': 'error',
  '@graphql-eslint/no-undefined-variables': 'error',
  '@graphql-eslint/no-unused-variables': 'error',
  '@graphql-eslint/no-unused-fragments': 'error',
  '@graphql-eslint/unique-operation-name': 'error',
  '@graphql-eslint/unique-fragment-name': 'error',

  // 最佳实践
  '@graphql-eslint/no-anonymous-operations': 'error',
  '@graphql-eslint/no-duplicate-fields': 'error',
  '@graphql-eslint/require-id-when-available': 'warn',

  // 命名规范
  '@graphql-eslint/naming-convention': ['error', { ... }],
}

Schema 感知规则

提供 schema 以实现高级验证:

{
  files: ['**/*.graphql'],
  languageOptions: {
    parser: graphqlPlugin.parser,
    parserOptions: {
      schema: './schema.graphql',
      // 或者
      schema: 'http://localhost:4000/graphql',
    },
  },
}

IDE 扩展

VS Code

GraphQL:语言功能支持GraphQL 基金会)

  • 语法高亮
  • Schema 类型的自动补全
  • 跳转到定义
  • 悬停文档
  • 针对 schema 的验证

配置(.graphqlrc.yml):

schema: "http://localhost:4000/graphql"
documents: "src/**/*.{graphql,ts,tsx}"

Apollo GraphQLApollo

  • Apollo 特有功能
  • Schema 注册表集成
  • 性能洞察

JetBrains IDE

GraphQL 插件:

  • 语法高亮
  • Schema 感知的补全
  • 验证
  • 导航到定义

配置(.graphqlconfig):

{
  "schemaPath": "./schema.graphql",
  "includes": ["src/**/*.graphql"]
}

配置文件

常见的配置文件名:

  • .graphqlrcJSON
  • .graphqlrc.ymlYAML
  • .graphqlrc.jsonJSON
  • graphql.config.jsJavaScript
# .graphqlrc.yml
schema: "http://localhost:4000/graphql"
documents: "src/**/*.graphql"
extensions:
  codegen:
    generates:
      ./src/generated/graphql.ts:
        plugins:
          - typescript
          - typescript-operations

操作验证

针对 Schema 进行验证

# 使用 graphql-inspector
npx graphql-inspector validate ./src/**/*.graphql ./schema.graphql

CI 集成

# .github/workflows/graphql.yml
name: GraphQL 验证

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 安装依赖
        run: npm ci

      - name: 下载 schema
        run: npx graphql-inspector introspect http://localhost:4000/graphql --write schema.graphql

      - name: 验证操作
        run: npx graphql-inspector validate './src/**/*.graphql' schema.graphql

      - name: 检查破坏性变更
        run: npx graphql-inspector diff schema.graphql http://localhost:4000/graphql

Pre-commit 钩子

// package.json
{
  "lint-staged": {
    "*.graphql": ["eslint --fix", "graphql-inspector validate ./schema.graphql"]
  }
}

操作复杂度检查

# 检查查询复杂度
npx graphql-query-complexity-checker \
  --schema ./schema.graphql \
  --query ./src/queries/GetUser.graphql \
  --max-complexity 100

持久化查询提取

生成用于生产环境的持久化查询:

// codegen.ts
const config: CodegenConfig = {
  generates: {
    "./persisted-queries.json": {
      plugins: ["graphql-codegen-persisted-query-ids"],
      config: {
        output: "client",
        algorithm: "sha256",
      },
    },
  },
};

输出:

{
  "abc123...": "query GetUser($id: ID!) { user(id: $id) { id name } }",
  "def456...": "mutation CreatePost($input: CreatePostInput!) { ... }"
}