集成Ovral,解决前端手抄API的痛点

技术相关105422 阅读0

当一个人又写接口又写前端的时候,最大的痛苦莫过于接口改动后还要修改前端项目并重新部署。

以前在项目少、接口不多的时候还没有感觉。现在项目多了之后,改动一个接口就要去使用这个接口的前端项目挨个改。尤其是加了TS之后,还要手动维护类型标注,头更痛了。

我司使用的是YAPI,研究一下发现太重了,并且配置复杂。我找到了一个更轻量、配置更少的解决方案: Ovral。

我的JAVA项目一开始就配置有swagger,能自动根据你接口上的注解生成api文档。而Ovral又正好能根据这个文档,在你的前端项目直接生成接口方法和TS的类型标注,不管是Vue项目还是React都能用。

基础配置

直接安装依赖:

npm install orval -D

在前端项目的根目录下新建orval.config.ts:

import { defineConfig } from 'orval';

export default defineConfig({
  projectName: {  // 这个名字可以随便改,推荐叫后端项目名
    input: 'https://xxxxxxx/v3/api-docs', // Java项目生成的接口文档
    output: {
      mode: 'single', // 不拆分文件,所有接口放在一个ts文件里
      target: 'src/api/generated/index.ts', // 生成的接口放在这个文件
      schemas: 'src/api/generated/models', //生成的ts类型放在这里
      indexFiles: true,
      clean: true // 每次拉取接口时是否清空上次生成的
    },
  },
  // 如果要从多个后端项目中拉取api,从这继续往下配置
});

最后在package.json中配置运行命令:

  "scripts": {
    "api": "orval"
  },

直接运行以下命令:

npm run api

理论上来说这时候就生成成功了,直接可以从src/api/generated/index.ts文件中import你需要的接口就行了。

进阶配置

很多前端项目应该都会自己封装一个拦截器来控制每个接口携带Token、统一处理接口报错等。

上面的基础配置默认使用自己的fetch来调用接口。如果你使axios,或者自己封装了拦截器,那么这时候我们就需要一个mutator了。

它相当于一个工具函数。配置之后,调用生成的接口就会将你传给接口的参数传给这个工具函数,然后你直接在这个工具函数中用这些参数给你的拦截器。这样每个接口都会经过拦截器了,对原有代码的功能和配置毫无影响。

我们直接新建一个mutator.ts:

import { request } from './fetcher'; // 引用你自己的拦截器

export const customInstance = <T>(config: {
  url: string;
  method: string;
  params?: Record<string, unknown>;
  data?: unknown;
  headers?: Record<string, string>;
}): Promise<T> => {
  let url: string = config.url;

  // 如果是GET/DELETE请求,将参数拼在请求地址后面
  if (config.params) {
    const searchParams = new URLSearchParams();
    for (const [key, value] of Object.entries(config.params)) {
      if (value !== undefined && value !== null) {
        searchParams.append(key, String(value));
      }
    }
    const qs = searchParams.toString();
    if (qs) url += `?${qs}`;
  }

  // 将接收到的参数给你的拦截器
  return request<T>(url, {
    method: config.method as 'GET' | 'POST' | 'PUT' | 'DELETE',
    body: config.data,
    headers: config.headers,
  });
};

最后在orval.config.ts添加上这个mutator

import { defineConfig } from 'orval';

export default defineConfig({
  projectName: {  
    input: 'https://xxxxxxx/v3/api-docs', 
    output: {
      mode: 'single', 
      target: 'src/api/generated/index.ts', 
      schemas: 'src/api/generated/models', 
      indexFiles: true,
      clean: true,
      override: {
        mutator: {
          path: 'src/api/mutator.ts',  // 刚才创建的文件
          name: 'customInstance', // mutator中方法的名称,根据这个生成代码,要和mutator文件中的方法保持一致
        },
      },
    },
  },

});

重新运行npm run api后我们来看下生成的结果:

// src/api/generated/index.ts

import { customInstance } from '../mutator';

export const getList = () => {
  return customInstance({
    url: `/api/list`,
    method: 'POST'
  });
}

结构很简单,如果接口是有参数的,它还会帮你自动进行标注,这样引用接口的时候还能提示需要哪些参数,必传但是没传的参数也可以被TS检查出来。

使用的时候直接在业务代码中引用生成的代码就行了:

import { getList } from "src/api/generated/index.ts"

getList().then((res) => {
  // xxx
})

总结

配置上非常简单,花一点时间就能配置好,带来的体验和提升的效率却相当巨大。

当然,前提是你的后端项目要有swagger或者类似的导出接口的功能。配置上也不难,配置之后还能方便的导入到Postman、ApiFox之类的api管理工具中。

Swagger不是唯一的解法,Orval也不是最终的答案。本文只是分析一种前端导入后端API的思路,具体的大家可以根据自身的项目来调整。

如果你有不同的想法,欢迎在评论区交流。

评论

发表评论