使用Apollo CLI可以从GraphQL模式自动生成TypeScript类型定义

为了减少在使用TypeScript时在GraphQL(Apollo Client)中的繁琐工作,我们可以使用Apollo CLI(apollo-tooling)从Schema自动生成TypeScript的类型定义,以避免在TypeScript的类型定义和Schema的定义中重复编写。

阿波罗命令行界面 – 阿波罗文档
阿波罗命令行界面 – Github

这个名字看起来是”Apollo CLI”,但在Github上的仓库名是”apollo-tooling”,在npm上是”apollo”,而且与”Apollo Client”也很相似,有点令人困惑。在本文中,我们将使用”apollo-tooling”这个名字。

安装 Apollo-Tooling(Apollo CLI)。

Apollo-tooling是一个工具,它使得可以在命令行上操作Apollo提供的各种库。

Apollo CLI 是一个 npm 包。

错误:无法从另一个模块或领域使用GraphQLSchema “[object GraphQLSchema]”。

在已经安装了”graphql”: “^14.5.8″的依赖项的情况下,当我额外安装了”apollo”: “^2.23.0″后,我在执行apollo-tooling命令时遇到了以下错误。

Error: Cannot use GraphQLSchema "[object GraphQLSchema]" from another module or realm.
Ensure that there is only one instance of "graphql" in the node_modules
directory. If different versions of "graphql" are the dependencies of other
relied on modules, use "resolutions" to ensure only one version is installed.
https://yarnpkg.com/en/docs/selective-version-resolutions
Duplicate "graphql" modules cannot be used at the same time since different
versions may have different capabilities and behavior. The data from one
version used in the function from another could produce confusing and
spurious results.

在查看 node_modules > apollo > package.json 时,发现 dependencies 中有一项 “graphql”: “14.0.2 – 14.2.0 || ^14.3.1” 的内容,而且在 node_modules > apollo > node_modules 下有一个 graphql 文件夹。
似乎这与现有的某些内容发生了冲突。
根据错误信息中提及的 “(使用 yarn) 使用 resolutions” 的指示,我在 package.json 中添加了以下内容,然后重新安装了 node_modules,错误成功解决。
如果有可能发生类似的错误,建议在执行安装之前提前添加这些内容到 package.json 中。

如果您使用npm,可能需要直接更改版本(作者未经实际测试)。

"resolutions": {
  "graphql": "^14.5.8",
  "graphql-tag": "^2.10.1"
},
// graphql-tagの重複エラーは見かけませんでしたが、重複インストールは発生していたのでついでに追加しました。

完成错误避免的准备后,通过以下命令进行安装。

yarn add -D apollo

# OR

npm install -D apollo

准备好apollo.config.js

按照官方网站的说明,在项目的根目录准备一个名为「apollo.config.js」的文件。
配置Apollo项目
这次,我们将从远程终点获取模式信息。

module.exports = {
  client: {
    name: 'client',
    includes: ['src/**/*.ts'],
    tagName: 'gql',
    addTypename: true,
    service: {
      // remote endpoint
      name: 'sever',
      url: 'https://server/graphql',
      headers: {
        authorization: 'nanikashiranohituyounatoken...',
      },
    },
  },
};

tagName是一个函数名,用于传递GraphQL查询的模板字面量。(默认值为gql)
在上述情况中,在.ts文件的src目录下,指示在gql函数的参数值中引用模板字面量。
此外,addTypename选项用于在生成类型定义文件时添加Schema中定义的类型名称。(默认值为true)

我在客户端的服务中指定了引用的服务器。

生成并执行类型定义

请在 package.json 中添加 script 并执行。
有各种选项可供选择。
请访问 https://github.com/apollographql/apollo-tooling#apollo-clientcodegen-output。

在中文中的本地化的翻译可以是:
“apollo client:codegen [OPTIONS] [OUTPUT]” 是用于定义输出命令的指令。
“[OUTPUT]” 是指输出文件的路径。如果没有特殊指定,则会在原始文件所在的目录下生成一个名为 “__generated__” 的文件夹,并且为每个查询生成一个定义文件。
在此过程中,如果查询名称重复,则会导致错误。

"scripts": {
  "apollo:codegen": "apollo client:codegen --target=typescript --globalTypesFile=src/types/globalTypes.ts"
}

除了TypeScript之外,还支持Swift、Flow和Scala,因此在–target=typescript中明确了该选项。
由于共享的类型定义文件(globalTypesFile)在生成时是放在根文件夹中(/__generated__/globalTypes.ts),所以将其指定到/src/types/文件夹下。

请提前下载Schema文件。

如果每次服务器访问时令牌过期时间很短等等麻烦的情况下,您可以事先将其下载到本地。

"scripts": {
  "apollo:download": "apollo client:download-schema src/type/schema.json",
  "apollo:codegen": "apollo client:codegen --target=typescript --localSchemaFile=src/type/schema.json"
}

阿波罗(Apollo):在使用apollo:download命令下载后保存到src/type/schema.json文件中,在执行apollo:codegen命令时会引用该文件。

自定义标量的兼容性

如果自定义标量(Custom Scalar)被独立定义了类型(例如日期),默认情况下会变成任意类型(any)。
如果在apollo:codegen的选项中使用–passthroughCustomScalars参数,那么Custom Scalar的名称将原样输出。

然而,由于globalTypesFile中并未定义相应的类型,因此您需要提前准备好自己的类型。

// 日付型の Custom Scalar の名前が GqlDate の場合
declare global {
  type GqlDate = Date;
}

而且,实际接受到的数据并不会被转换成Date类型,所以需要单独进行数据类型转换处理。

// 「GqlDate」が、「ISO 8601」形式の文字列型で送信されるとした場合

// GqlDate(ISO 8601)判定
export const isGqlDate = (arg: any): arg is GqlDate => {
  return (
    typeof arg === 'string' &&
    /^\d{4}(-\d\d){2}T\d{2}(:\d\d){2}.\d{2,3}Z$/.test(arg)
  );
};

// GqlDate文字列をDateオブジェクトに変換
export const convertGqlDateToDate = <T extends Object = {}>(arg: T): T => {
  let res: { [key: string]: any } = {};

  for (const [key, value] of Object.entries(arg)) {
    if (isGqlDate(value)) {
      res[key] = new Date(value);
    } else if (Object.prototype.toString.call(value) === '[object Object]') {
      res[key] = convertGqlDateToDate(value);
    } else if (Array.isArray(value)) {
      // GqlDate[] には対応していない……
      res[key] = value.map(item => {
        if (Object.prototype.toString.call(value) === '[object Object]') {
          return convertGqlDateToDate(item);
        }
        return item;
      });
    } else {
      res[key] = value;
    }
  }
  return res as T;
};

// DateオブジェクトをGqlDate(ISO 8601)に変換
export const convertDateToGqlDate = <T extends Object = {}>(arg: T): T => {
  let res: { [key: string]: any } = {};

  // ...略

  return res as T;
};

// variablesとonCompletedで変換処理を挟む

const [state, setState] = useState<StateType | null>(null);

const {loading, error} = useQuery<ResultType, VariablesType>(
  ANY_QUERY,
  {
    variables: convertDateToGqlDate<VariablesType>(anyData),
    onCompleted(result) {
      setState(
        convertGqlDateToDate<StateType>(result.something)
      );
    },
  },
);

关于在Apollo Client中引入自定义标量,似乎有人正在创建库。
正式引入还需要些时间吧。。。

请提供相关信息

使用Apollo Codegen自动为GraphQL查询生成TypeScript定义
为GraphQL数据使用TypeScript和Apollo Client生成类型
使用Apollo工具自动生成TypeScript客户端的类型定义

bannerAds