跳转到内容

如何实现从后端“接口文档”到前端“API函数”的自动化编码?

前端不再需要手动管理“API函数层”的代码,开发者只需要关注业务代码,可以做到“无感知”接口的存在。

文字的描述可能不太好理解,我们先来体验一下具体的使用场景吧。

快速体验

假设我们前端项目的API层代码是这样的:

ts
// /api/user.ts
import request from '@/api/request';
import { CreateUser, User } from './models';

/**
 * 创建用户
 * @param user
 * @returns 
 */
export function create(user: CreateUser) {
  return request<User>({
    url: '/api/user/create',
    method: 'post',
    data: user,
  });
}
ts
// /api/models/CreateUser.ts
/**
 * 创建用户信息模型
 */
export default interface CreateUser {
  /** 姓名 */
  name: string;
  /** 年龄 */
  age: number;
}
ts
// /api/models/User.ts
/**
 * 用户信息模型
 */
export default interface User {
  /** 姓名 */
  name: string;
  /** 年龄 */
  age: number;
  /** 角色 1.管理员 2.普通用户 */
  role: number;
  // ...
}
ts
// /api/models/index.ts
export * as CreateUser from './CreateUser.ts';
export * as User from './User.ts';

我们在业务代码中通常会这样使用:

ts
import * as userApi from '@api/user';

// 省略其他代码
const userData = await userApi.create({
  name: '张三',
  age: 35,
});

// 判断用户角色
console.log(userData.role === 1);

我们可以看到,API层的代码其实是“非常繁琐”的。你不仅需要对每一个接口写一个API函数,还需要写一大堆的数据模型TypeScript定义。

在实际开发中,可能很多开发者都是直接使用any代替了数据模型的定义,这对后期接口维护时会带来很大的负担。一旦接口字段变了,还可能会引起前端项目一些隐藏的bug。

那么试想一下,所有API层的代码都是根据后端接口文档来写的,那为什么不能用一款工具来帮我们自动完成这一步呢?

对于开发者来说,调用接口最理想的方式是:根本不用关心API层面的代码,只需要“拿来使用”即可

因此,我们可能需要这样一款工具:

  • 📌 输入后端符合OpenAPI规范的“接口文档”
  • 📌 根据文档自动生成API函数和数据模型定义
  • 📌 接口变更后支持同步更新
  • 📌 可以很方便地集成到项目中去
  • 📌 不要改变现有的编码习惯

现在,我们就将此工具命名为:UnoAPI

以后,你的项目可能并不需要API层了。它没有真正地干掉API层的代码,而是可以让开发者做到“无感知”API层的存在。

工作方式

虽然名字上与“UnoCSS”很相似,但它们的工作方式完全不一样:

  • **UnoCSS 是“先使用,后生成”:**它会根据你写的class类名,然后生成相应的CSS样式;
  • 而**UnoAPI必须是“先生成,后使用”:**它必须先生成API层的代码,然后才能使用,这必须符合我们的编码直觉和规范。

为什么UnoAPI必须是“先生成,后使用”?

因为你不可能调用一个“不存在”的函数以此来欺骗代码的正确性验证,这显然不符合编码逻辑。

但也不尽然!

我们完全可以先只生成TypeScript类型定义部分的代码,具体的实现我们也可以根据“先使用,后生成”的逻辑,来最小化生成我们的API层代码。

但是这样做又有多大的意义呢?

就为了使生成的代码最小化?但是现在的打包工具对Tree Shaking已经做得够好了,我们只需要生成符合Tree Shaking规范的API层代码就行了。

使用方式

为了得到更好的使用体验,我们可以参考Eslint工具的使用方式。

  1. 安装依赖包
  2. 在项目根目录定义配置文件
  3. 支持命令行进行操作
  4. 支持VS Code扩展操作

所以UnoAPI可以拆分成3个模块:

  • ✅️ 核心层:@unoapi/core;
  • ✅️ cli模块:@unoapi/cli;
  • ⭕️ VS Code扩展:@unoapi/vscode-extension;

为了体验到完整的功能,核心层与cli模块是必须的,VS Code扩展可以作为后期目标,提升用户体验。

我们先从使用的角度,将UnoAPI进行一个大致的设计:

1. 安装依赖包

bash
npm i @unoapi/core @unoapi/cli -D

2. 定义配置文件

我们将配置文件命名为:unoapi.config.js,通过命令行一键生成初始配置:

bash
uno init
# 在根目录生成 unoapi.config.js 默认配置

我们必须在配置文件中指定接口文档的OpenAPI地址,支持配置输出目录、

3. 生成API层代码

bash
uno api url1,url2,url3

根据接口的URL路径生成对应的API层代码,这里需要对URL格式进行一定的规范。

URL格式:模块1/模块2/.../动作

  • 模块路径:决定了默认的目录和文件名称
  • 动作:生成默认的API函数名称

比如: /project/group/getList/project/group/create,生成的API函数是这样的:

ts
// /api/project/group.ts

export function getList() {
  // ...
}

export function create() {
  // ...
}

对于不遵守此规范的URL,或者不想使用默认行为的,我们也需要支持自定义配置。

bash
uno api api/v2/aabbcc -o project/group.ts --func createProjectGroup

生成的API函数:

ts
// /api/project/group.ts

export function createProjectGroup() {
  return request({
    url: 'api/v2/aabbcc'
  })
}

为了更好的体验,我们可能还需要支持更多的操作命令,以及更加灵活的配置。但是这里先不讨论太细节的东西,我们只需要对UnoAPI设计一个大概的雏形即可。

持续更新

我创建了一个合集「UnoAPI工具篇」,这个系列将会持续更新。

在这个合集中,我将和大家一起来实现UnoAPI,共同见证一下它到底好不好用!