在早些年(约 2021 年)的时候就 介绍过 使用 proto 来做 IDL 生成 ts 代码,经过最近一段时间的使用,对这个使用流程做了一些优化。

我们利用 proto 来作为前后端的接口文档类似于下图这样:

我们后端使用的是 grpc-gateway ,这里前端这边使用 他 提供的 open-api-v2 插件生成 swagger 在通过 swagger-typescript-api 生成对应的 ts 代码。

有同学可能会提出问题为什么不直接使用 proto 来作为前后端通讯的方法,而要兜圈子,用 swagger 来作为中间媒介生成代码。这里在重新讲一下。

时至今日,我们依然可以发现 grpc-web 在 2024 年仍然没有成熟的框架+较好的调试工具来辅助开发,对比 json 形式的 restful api,其易用性 仍然需要打一个巨大的问号,对于需要投入生产的商业项目,在这点上不愿意以此为基础冒险使用一个 前景很不明朗 的项目。

流程管理

我们对于 proto 的流程大体上在编写代码前,我们会和后端一起约定 proto ,当 proto 约定完成以后合并到指定的分支,此时会触发 ci/cd 构建 go 代码并通知 ts 构建服务开始工作,ts 构建服务是一个中心化的 mono 大仓,储存了所有项目使用的 proto 产物。

ts 构建服务完成构建以后会提交到代码仓库作为备份,同时推送到内网的 npm 上。

此时前端就可以通过 @taptap/proto-xxx 安装并使用对应的接口。

构建脚本

构建脚本是一个比较简单的事情,我们使用一份较大的 setting.ts 作为配置文件管理所有的配置项目, 相关的脚本使用 ts-node 配合带类型的 zx 运行这样可以少写一下代码:

interface ProjectBasicSetting {
  /**
   * 项目的名称
   */
  name: string;
  notification?: {
    /**
     * 成功后通知的 slack 配置
     */
    slack?: {
      channel: string;
      thread_ts?: string;
    };
  };
  /**
   * 核心维护人员,如果项目失败,会通知这些人
   */
  maintainer?: string[];
}
export interface ProtoProject extends ProjectBasicSetting {  
  /**
   * git 仓库设置
   */
  git: {
    /**
     * 需要监控拉取的分支
     */
    branch: string;
    /**
     * git 仓库地址
     */
    repo: string;
    /**
     * glob 配置,用于监控的文件, 使用 glob 语法
     * 可以通过 input 来控制你需要处理的文件
     */
    glob?: {
      input?: string | string[];
    };
  };
  /**
   * 生成的 protoSetting 不建议配置, 如非必要请使用默认配置
   */
  protoSetting?: Record<string, string>;
}
export interface OpenApiProject {
	url: string;
}
export type ProjectSetting = ProtoProject | OpenApiProject;
 

这样我们的构建脚本就可以通过读取配置来完成代码封装,核心代码是下面这样写,其他的拉取代码操作就是简单的 git 操作,不多赘述。

/**  
 * 默认的 proto 生成 swagger 配置  
 */  
const defaultProtoSetting = {  
  // 合并 swagger  
  allow_merge: 'true',  
  // 合并文件名  
  merge_file_name: 'api',  
  // 不使用 json 名称  
  json_names_for_fields: 'false',  
  // 使用 fqn 命名策略  
  openapi_naming_strategy: 'fqn',  
};
// 其中 setting: ProtoProject, ctx: CTX,ctx 是一个上下文环境,用来获取一些 logger 什么的配置
// 拉取代码的操作不多赘述自行使用 zx 编写即可
// await fetchGit(setting, ctx);
const protoSetting = createProtoSetting({  
  ...defaultProtoSetting,  
  ...setting.protoSetting,  
});
const gitName = setting.name;
const inputs = setting.git?.glob?.input ?? ['api/**/*.proto'];
// 查找 proto 文件
const protoFiles = (
await glob(inputs, {
  cwd: `${ctx.cwd}/${gitName}`,
})
).map((p) => `${gitName}/${p}`);
// 使用 protoc 生成 swagger.json,注意这里 protoFiles 应当使用数组,否则会被 shell 解析为单个参数
ctx.logger.log(`filePaths: ${protoFiles.join(' ')}`);
const swaggerPath = `./packages/${outputName}/src`;
const { stdout, stderr, exitCode } = await $({
nothrow: true,
quiet: true,
})`protoc -I ./${gitName} --openapiv2_out=${swaggerPath} --openapiv2_opt=${protoSetting} ${protoFiles}`;

注意事项

  1. zx 中调用 git 不能使用 promise.all 因为 git 本身是 带锁的,同时调用 会抛出异常 提示 ‘.git/index.lock’: File exists
  2. openapi_naming_strategy 建议使用 fqn 否则碰到重名会比较麻烦,会出现互相覆盖的问题

在上面的 proto 将会生产一个 swagger.json 文件到 src 目录下面,随后按照自己的喜好编写 swagger-typescript-api 的模板,在编译 swagger 到 ts 代码即可,如果有需要的话,最后一步使用 tsc 简单的编译为 js + d.ts 提升在开发环境下的 ts 提示性能。

生成结果

type WithApiKey = { apiKey?: string };
export class Api<AdditionalConfig extends WithApiKey> {
  private _request: Request<AdditionalConfig | WithApiKey>;
 
  constructor(request: Request<AdditionalConfig | WithApiKey>) {
    this._request = request;
  }
 
  xxxXxx = {
    /**
     * No description
     *
     * @tags xxxService
     * @name XxxServiceXxxXxx
     * @request GET:/rep-admin/v1/basic/apps
     */
    xxxServiceXxxXxx: (query: XxxParams) =>
      this._request.GET<XxxResp>(`/xxx/xxx/xxx/xxx`, {
        params: query,
        config: { apiKey: 'XxxServiceXxxXxx' },
      }),
  }
}

随后在项目里面配合 @taptap/tool-kit-fe-vue 使用即可:

import { Api } from '@taptap/proto-xxx'
const apiService = new Request<{ apiKey>: string }>({ baseUrl: '' });
export const { xxxXxx } = new Api(apiService);
 
// 项目里面使用
import { useRequest } from '@taptap/tool-kit-fe-vue';
const { data, send, sending } = useRequst(xxxXxx.xxxServiceXxxXxx)
send();
// data -> XxxResp

本文标题:基于 OpenAPI 规范的前后端接口开发工具链

永久链接:https://iceprosurface.com/code/openapi-based-api-development-toolchain/

作者授权:本文由 icepro 原创编译并授权刊载发布。

版权声明:本文使用「署名-非商业性使用-相同方式共享 4.0 国际」创作共享协议,转载或使用请遵守署名协议。

查看源码: