使用代码生成器将 TypeScript 的类型定义在前端和服务器之间共享的方法

前端使用React或Vue框架,使用TypeScript进行开发,后端使用Node.js,也使用TypeScript进行开发。
在采用这种架构时,经常会出现的问题是在API调用的逻辑中,前端和后端都重复定义了类似的数据类型。

如果在前端和服务器上直接复制并使用同一种类型,最初可能还好,但在修复错误或添加功能时很容易忘记复制,结果可能导致不同版本的差异。

即使为了类型定义而创建一个共享的存储库,每次都要更新存储库,然后在前端和服务器两者中都要升级版本,这样的工作非常繁琐。

那种时候的烦恼和困扰能够迅速解决的方便工具就是Code Generator。
只要使用一次,就会觉得为什么一开始开始使用TypeScript的时候没有做这个工具呢,开发会变得非常轻松,所以推荐使用。

Code Generator 是什么?

Code Generator是什么意思?

    • REST APIであればswagger.json

 

    GraphQLであればshema.json

有一些工具可以根据输入调用服务器端的终端,自动生成函数和输入/输出类型等。有许多工具可供选择,例如

    • REST APIであればOpenAPI Generator

GraphQLであればGraphql Code Generator

以下是一些例子。

使用OpenAPI Generator的方法

准备swagger.json文件

首先,我们需要在服务器端定义swagger.json。
如果服务器端是NestJS,只需使用Class定义请求和响应的类型,就可以自动从中生成swagger.json,非常推荐。
参考链接:https://docs.nestjs.com/openapi/cli-plugin#using-the-cli-plugin

安装OpenAPI Generator CLI

在调用API的源头(本例中是前端)上,您可以通过以下命令安装Open API Generator CLI。

$ npm install --save-dev @openapitools/openapi-generator-cli

执行命令

准备下述命令的npm脚本,然后执行npm run codegen。

"scripts": {
  "codegen": "openapi-generator-cli generate -i http://<your domain>/swagger.json -g typescript-axios -o ./codegen/api-client"
}

那么,代码将自动在 ./codegen/api-client 文件夹中生成。
每个选项的意义如下说明。

オプション概要-i対象となるswagger.jsonのurl-g自動生成されるファイルのタイプ-o自動生成されたファイルの出力先

顺便一提,对于NestJS,如果使用–remove-operation-id-prefix选项来删除swagger.json的operationId前缀,方法名会变得更加清晰。

4. 试着调用自动生成的代码。

假设服务器端的代码是用NestJS编写的,如下所示。

export class CreateUserBodyDto {
  email: string;
}

export class CreateUserResponseDto {
  name: string;
}
@Controller('users')
export class UsersController {
  @Post()
  createUser(@Body() createUserDto: CreateUserBodyDto): CreateUserResponseDto {
    return { name: 'Test' };
  }
}

当您使用此代码生成swagger.json并执行codegen命令时,您可以使用生成的代码来调用API,如下所示。

import { Configuration, DefaultApi, CreateUserBodyDto } from '../codegen/api-client';

export class ApiClientRepository extends DefaultApi {
  constructor() {
    super(new Configuration({ basePath: 'http://<your domain>/' }));
  }
}
import { CreateUserBodyDto } from '../codegen/api-client';

const apiClientRepository = new ApiClientRepository();
const input: CreateUserBodyDto = { email: 'test@qiita.com' };
const { name } = apiClientRepository.createUser(input);

console.log(name);

在中国创建用户的方法中,参数和返回值都已定义了类型并且可以进行导入。这意味着可以在前端使用服务器端的类型定义而无需重新定义。

Graphql Code Generator的用法如下

准备一个schema.json文件

首先,在服务器端定义graphql的schema.json。如果使用NestJS作为服务器端,并采用code first的方法引入graphql,只需通过在Class中定义请求和响应的类型,就可以自动生成schema.json,因此建议使用该方法。详情请参考:https://docs.nestjs.com/graphql/quick-start#overview

安装 GraphQL Code Generator

在API的调用源(本例中为前端)上通过以下命令安装Graphql代码生成器。

$ npm install --save-dev @graphql-codegen/cli

如果要在 TypeScript 和 React(+Apollo client)中使用,请安装以下插件,并同时更新 codegen.yml。

$ npm install --save-dev @graphql-codegen/typescript @graphql-codegen/typescript-resolvers @graphql-codegen/typescript-react-apollo
overwrite: true
schema: 'http://<your domain>/graphql'
documents: 'src/**/*.graphql'
generates:
  ./src/graphql/types.ts:
    plugins:
      - 'typescript'
      - 'typescript-resolvers'
      - 'typescript-react-apollo'

执行命令

假设服务器端是用NestJS编写的以下示例代码。

@ArgsType()
export class SampleArgsDto {
  @Field(() => String, { nullable: true })
  sampleId?: string;
}

@ObjectType()
export class SampleData {
  @Field()
  sampleId: string;
}
@Resolver()
export class SampleResolver {
  @Query(() => SampleData)
  sample(@Args() { sampleId }: SampleArgsDto): SampleData {
    return { sampleId };
  }
}

根据此代码,在前端准备以下类型的查询。

query getSample($sampleId: String) {
  sample(sampleId: $sampleId) {
    sampleId
  }
}

然后在npm脚本中准备以下命令,并执行npm run codegen。

"scripts": {
  "codegen": "graphql-codegen --config codegen.yml"
}

然后,会在./graphql文件夹中自动生成代码。

4. 尝试调用自动生成的代码。

使用在前一步生成的代码,可以调用以下的查询。

import React, { useEffect } from 'react';
import { useGetSampleQuery } from '../graphql/types';

const Sample = () => {
  const { data } = useGetSampleQuery({
    variables: { sampleId: 'init' },
  });

  return (
    <div>Response: {data?.sample.sampleId}</div>
  );
};

export default Sample;

不仅仅是定义了模型结构,还创建了可以调用查询并获取数据的钩子,因此前端只需要编写查询即可,非常简单。

综上所述

REST API和GraphQL是完全不同的技术,但是通过使用代码生成器,在前端和服务器之间可以共享类型定义。
此外,还可以自动生成用于调用端点的代码,让实现变得非常容易。
虽然两者都需要一些繁琐的准备工作,但一旦引入,后续的开发将变得非常容易,所以我推荐使用。
但是请注意,一旦开始使用,就很难停止,所以在引入时请注意。

bannerAds