Graphql Codegen - 自动生成 TS 类型

引言

个人项目 《仿 Mac 个人网站开发 |项目复盘》 很早之前就用上了 Graphql, 近期对前端项目整体做了升级(转 NextJS、引入 react-query)! 在翻代码时看到之前配置的 Graphql Codegen 然后就是一脸懵逼(时间太久了, 都忘记每项配置的作用了...), 所以就重新查阅了下官网, 对这一块配置也进行了升级, 整个流程又重新走了一遍! 故而就有了本篇文章!!

项目源码: github.com/KunLunXu-CC...

一、什么是 Graphql Codegen

首先 Graphql Codegen 是一个 开发/构建 工具, 它主要的作用其实就是帮助我们自动生成 TS 类型

具体的来讲就是, 假设你的后端是 Graphql 服务, 并且在前端你是通过常规编写 Graphql 方式来调用接口的, 那么通过 Graphql Codegen 能够帮助你减少大量工作, 该工具可以监听 Graphql 操作, 并根据指定的 Graphql 服务, 生成所有必备的接口响应、请求参数等一些列 TS 类型, 甚至可以帮助你快速生成 useQueryuseMutationhooks!!

有了它可以大大减少您必须编写的样板代码, 使得使用 GraphQL API 变得更加容易, 便捷! 并且通过简单的配置, 可以让 TS 的发挥最大化(智能提示)

二、配置讲解

其实官方已经提供了很 详细的文档, 本文只是做了些适当的简化、和说明, 所以在整个过程中, 如果遇到任何问题, 请以 官方 为准!!!!

2.1 基础配置

  1. 安装 @graphql-codegen/cli 依赖: 提供生成代码所必要的一些 CLI 工具
sh 复制代码
pnpm add --save-dev @graphql-codegen/cli
  1. 项目 根目录 下创建配置文件 codegen.ts
ts 复制代码
import type { CodegenConfig } from "@graphql-codegen/cli";

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

export default config;

配置项说明:

  • schema: xxxx 用于指定后端 Graphql 服务地址, 我这边 后端服务 地址为 http://localhost:4000/api/graphql
  • documents 指定当前 前端 项目中可能包含 Graphql 操作的文件, 这个文件中如果匹配到 Graphql 操作代码, 则会自动生成我们预期的 TS 类型代码
  • generates 定义要生成的内容, 该配置是一个对象, 对象的 key 是生成内容文件对应的路径(可以是具体的文件、也可以是个目录), 而值则是生成的配置项(规定如果生成代码、生成怎么样的代码), 这里的配置和 Babel 有点像, 都是可以指定 presetplugins 等内容
  • 更多配置内容参考: docs/config-reference/codegen-config

2.2 测试效果

是的没错, Graphql Codegen 配置很简单, 就简单的两步即可, 下面我们来看下它的效果(作用)!

  1. 随便找个地方, 写个页面(我这里是 NextJS 项目)
ts 复制代码
"use client";

const Demo = () => {
  return <div>11111</div>;
};

export default Demo;
  1. 下面问题来了, Graphql Codegen 是如何识别出文件中包含的 Graphql 操作代码呢? 其实就是通过一些特定的标识、写法、文件名, 来识别的。下面是我所知道的几种写法
  • 调用函数 graphqlgql, 其参数 字符串 会被视为 Graphql 操作代码, 即便函数没有定义(因为并没执行代码, 无所谓有没定义)
ts 复制代码
// graphql 函数的字符串参数会被视为 Graphql 操作代码, 被识别到
const getUserInfo = graphql(`
  query getUserInfo {
    userInfo {
      user {
        id
        name
      }
      message
    }
  }
`);


// gql 函数的字符串参数会被视为 Graphql 操作代码, 被识别到
const getUserInfo = gql(`
  query getUserInfo {
    userInfo {
      user {
        id
        name
      }
      message
    }
  }
`);
  • 通过注释 /* GraphQL */ 将一串字符串标记为 Graphql 操作代码
js 复制代码
// 注释 /* GraphQL */ 后面字符串, 会被视为 Graphql 操作代码, 被识别到
const gql = /* GraphQL */ `
  query getUserInfo {
    userInfo {
      user {
        id
        name
      }
      message
    }
  }
`;
  • 特定文件: 对于所有以 .gql.graphql 作为后缀名的, 文件内容会被视为 Graphql 操作代码, 被识别到
gql 复制代码
query getUserInfo {
  userInfo {
    user {
      id
      name
    }
    message
  }
}

这里需要注意的是: 上面 codegen.ts 配置中 documents 并没有包含 .gql.graphql 文件, 这里如果要测试, 记得补充上!!!

  1. 触发生成: 通过执行 raphql-codegen --config codegen.ts 命令, 可触发代码生成, 如下:
sh 复制代码
npx graphql-codegen --config codegen.ts

执行命令后将生成相应的代码

  • graphql/fragment-masking.ts: 使用 Graphql 片段所需要用到的 TS 以及 hooks
  • graphql/gql.ts: 代码中的所有 Graphql 操作对应 documents, 同时还导出了一个方法, 用于获取对应 Graphql document
  • graphql/graphql.ts: 根据后端 Graphql 服务生成的, 接口数据类型、参数
  • graphql/index.ts: 入口文件, 导出所有用到的方法

2.3 使用: 如何发起一个请求

下面我们就可以使用 @tanstack/react-querygraphql-request 来简单演示下如何使用 Graphql Codegen 生成的产物来发起一个简单的请求!

如下代码所示:

  • 直接在文件顶部编写 Graphql 操作
  • 通过 graphql-request 发送 Graphql 请求, 需要注意的是新版本的 graphql-request 不再支持相对路径, 具体可以看这篇 Issues Support passing relative path for URL #745
ts 复制代码
"use client";
import { graphql } from "@/graphql";
import request from "graphql-request";
import { useQuery } from "@tanstack/react-query";

// 1. 编写 Graphql 操作
const getUserInfoDocument = graphql(`
  query getUserInfo {
    userInfo {
      user {
        id
        name
      }
      message
    }
  }
`);

const Demo = () => {
  // 2. 请求接口
  const { data } = useQuery({
    queryKey: ["getUserInfo"],
    queryFn: async () => request("http://localhost:3000/api/graphql", getUserInfoDocument),
  });

  console.log(data);

  return <div>11</div>;
};

export default Demo;

开始前, 我们先执行 raphql-codegen --config codegen.ts 命令, 生成相关代码

sh 复制代码
npx graphql-codegen --config codegen.ts

最后启动项目, 并访问 DEMO 页面, 不出意外的话, 在控制台应该是可以看到预期的结果

2.4 简单请求函数封装

上文我们是直接调用 graphql-request 来发起请求的, 然而在实际项目中! 肯定不能这么整, 我们还是需要简单封装一个工具函数, 最些简单的封装, 目的其实就是为了处理一些个性化配置, 比如请求路径处理、请求头处理、参数处理、响应错误处理.....

如下所以, graphql-request 允许我们创建一个自己的实例, 在创建实例时我们就可以进行一些简单的配置

js 复制代码
import { GraphQLClient, ClientError } from "graphql-request";
import { createApi, BaseQueryFn } from "@reduxjs/toolkit/query/react";

// see: https://github.com/prisma-labs/graphql-request
const client = new GraphQLClient("http://localhost:3000/api/graphql", {
  mode: "cors",
  credentials: "include",
});

export default client;

调用方式也简单, 只需要调用 client.request 即可, 正如下文你所看到的, 一切都变得更加简单...

ts 复制代码
const { data } = useQuery({
  queryKey: ["getUserInfo"],
  queryFn: async () => client.request(getUserInfoDocument),
});

三、集成 VSCode

既然, 我们能够生成所有 Graphql 服务的相关 TS 配置, 那么自然要发挥其最大的作用!!! 我们希望在编写 Graphql 操作片段时, 能够提供完备的智能提示!

3.1 生成「GraphQL」服务模型

首先我们需要一份 GraphQL 后端服务的完整模型架构, 这里我们一样可以直接通过 Graphql Codegen 来帮助我们快速生成!

这里我们需要先通过 @graphql-codegen/schema-ast 插件来生成 GraphQL 后端服务的完整模型架构!! 下面我们来安装下 @graphql-codegen/schema-ast

sh 复制代码
pnpm add @graphql-codegen/schema-ast -D

接下来修改 codegen.ts 配置文件: 使用 @graphql-codegen/schema-ast 插件, 输出 GraphQL 服务模型到 schema.graphql 文件中

diff 复制代码
import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  schema: "http://localhost:4000/api/graphql",
  documents: ["src/**/*.tsx"],
  ignoreNoDocuments: true,
  generates: {
    "./src/graphql/": {
      preset: "client",
    },
+   "./schema.graphql": {
+     plugins: ["schema-ast"],
+     config: {
+       includeDirectives: true,
+     },
    },
  },
};

export default config;

最后执行 npx graphql-codegen --config codegen.ts

sh 复制代码
npx graphql-codegen --config codegen.ts

最终会在项目根目录下创建预期的文件:

3.2 VSCode 配置

到目前我们只是生产了一份 GraphQL 服务模型, 但并没有将其运用起来, 下面我们需要做的就是借助 @0no-co/graphqlsp 来帮助我们更好的编写代码

  1. 安装依赖包 @0no-co/graphqlsp
sh 复制代码
pnpm add @0no-co/graphqlsp -D
  1. 修改 tsconfig.json 配置: 使用 @0no-co/graphqlsp
diff 复制代码
{
  "compilerOptions": {
    ....
    "plugins": [
+     {
+       "name": "@0no-co/graphqlsp",
+       "schema": "./schema.graphql"
+     }
    ],
  },
  ...
}
  1. 修改 .vscode/settings.json
diff 复制代码
{
  ....
+ "typescript.tsdk": "node_modules/typescript/lib",
+ "typescript.enablePromptUseWorkspaceTsdk": true
}

3.3 测试效果

到此本小节配置结束, 下面看下效果吧!

  1. 编写过程中, 会给出所有可选内容
  1. 对于不存在的字段, 会给出提示和建议
  1. ...

四、更多优化配置

到处本文的目的基本完成, 后面则是一些个人的一些配置上的优化!

4.1「Npm」脚本配置

为命令 npx graphql-codegen --config codegen.ts 配置一条 Npm 脚本, 毕竟每次手打也很麻烦!!!

所以这里我就增加有一条 Npm 脚本, 如下所示调整 package.json

diff 复制代码
{
  "name": "klx-website",
  ...
  "scripts": {
    "dev": "next dev",
    "cz": "klx-cz",
    "release": "klx-release",
    "prepare": "husky install",
+   "gql-codegen": "graphql-codegen --config codegen.ts"
  },
}

后面如需手动执行 Graphql Codegen 只需要执行:

sh 复制代码
npm run gql-codegen

4.2 开启监听模式

每次修改完 GraphQL 操作代码, 总是要手动执行 Graphql Codegen CLI 命令肯定是不靠谱的!

更好的体验肯定是只要我们修改了 GraphQL 操作代码, 在保存内容后, 能够自动执行 Graphql Codegen CLI 完成代码的生成!!

要做到这一步, 我们还需要借助 @parcel/watcher 工具! 所以我们先把该依赖包下载下来!

sh 复制代码
pnpm add @parcel/watcher -D

接下来我们就可以为 graphql-codegen 命令添加 --watch 参数, 开启监听模式, 如此只要 GraphQL 操作代码发生修改, 就会触发 graphql-codegen 命令, 生成新的代码!

最后调整下 package.json 种的 Npm 脚本: 希望在执行 dev 脚本(跑项目)的同时, 能够开启 graphql-codegen

diff 复制代码
{
  "name": "klx-website",
  ...
  "scripts": {
+   "dev": "next dev & npm run gql-codegen -- --watch",
    "cz": "klx-cz",
    "release": "klx-release",
    "prepare": "husky install",
+   "gql-codegen": "graphql-codegen --config codegen.ts"
  },
}

五、参考

相关推荐
EasyNTS4 分钟前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜2 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点2 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow2 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o2 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic3 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā3 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年4 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder4 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727574 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架