最好用的 生成 TypeScript 类型和 API 请求代码的工具
orval 的目标是为你生成整个 API 调用层。你给它一个 OpenAPI 规范,它还你一整套可以直接在代码中导入和使用的 TypeSafe 的 API 函数。
各个库的优缺点 推荐使用 orval
| @umijs/max-plugin-openapi | openapi-ts-request | orval | |
|---|---|---|---|
| 优点 | 配合umi配置简单 | 配置简单快速 | 适合大型项目,生成的类型很齐全,包括枚举都会生成 |
| 缺点 | 生成的类型简单(不会生成枚举或按文件生成) | 生成的类型简单(不会生成枚举或按文件生成) | 灵活性相对较低 |
orval 主要好处
- 开箱即用,极高效率 适合大项目和新项目
- 类型安全全覆盖
- 强大的集成
- 高度可配置
- 现成配置 支持
angular-appreact-appreact-queryvue-querynext-app等 真正的开箱即用。
安装
pnpm add orval --save-dev
配置文件 orval.config.js
module.exports = {
api: {
input: 'http://localhost/api-doc/openapi.json',
output: {
mode: 'split',
target: './src/services/openapi.ts',
schemas: './src/services/models',
override: {
mutator: {
name: 'customClient',
path: './src/services/request.ts',
}
},
mock: false,
prettier: true,
},
},
}
自定义请求函数
不使用 orval提供的请求函数,自定义请求函数 需要是RequestConfig类型
interface RequestConfig {
method: 'get' | 'put' | 'patch' | 'post' | 'delete';
url: string;
params?: any;
data?: any;
responseType?: string;
}
- override.mutator.name 自定义请求函数 导出的请求函数名称。
- override.mutaror.path 自定义请求函数 的路径。
其中自定义请求函数。如果有params参数的, 用 queryString 或者自定义拼接请求参数。
const queryString = params?`?${new URLSearchParams(
Object.entries(params).reduce(
(acc, [key, value]) => {
if (value !== undefined) {
acc[key] = String(value)
}
return acc
},
{} as Record<string, string>
)
).toString()}`
: ''
运行脚步
- 自定义nodeJS 脚本 orval.ts
exec('npx orval --config orval.config.cjs', (error, stdout, stderr) => {
if (error) {
console.error('Exec error:', error);
}
if (stderr) {
console.error('Stderr:', stderr);
}
if (stdout) {
console.log('Stdout:', stdout);
}
});
- NPM 执行脚本
"script": {
"orval" : "orval --config ./orval.config.js",
"orval-ts": "ts-node ./orval.ts"
}
自定义openApi-plugin
因为 @umijs/max-plugin-openapi 这个插件自带的功能不支持生成枚举,在编码的时候想直接用枚举就不行。索性自定义以一个查看以实现以上功能。
在config 目录下面创建 plugins/openApi-plugin.ts 具体代码如下
import { IApi } from '@umijs/max';
import { generateService, GenerateServiceProps } from '@umijs/openapi';
import pkg from '../../package.json';
export default (api: IApi) => {
api.describe({
key: 'openAPI',
config: {
schema(joi) {
return joi.alternatives().try(
joi.object({
schemaPath: joi.string().required(),
projectName: joi.string(),
namespace: joi.string(),
requestLibPath: joi.string(),
serversPath: joi.string(),
mock: joi.boolean(),
enumStyle: joi.string().valid('string-literal', 'enum'),
splitDeclare: joi.boolean(),
}),
joi.array().items(
joi.object({
schemaPath: joi.string().required(),
projectName: joi.string(),
namespace: joi.string(),
requestLibPath: joi.string(),
serversPath: joi.string(),
mock: joi.boolean(),
enumStyle: joi.string().valid('string-literal', 'enum'),
splitDeclare: joi.boolean(),
}),
),
);
},
},
});
api.registerCommand({
name: 'openapi',
description: pkg?.description,
fn: async ({ args }) => {
const config = api.config.openAPI || {};
const configs = Array.isArray(config) ? config : [config];
for (const cfg of configs) {
api.logger.info(`[openApi] 正在生成: ${cfg.projectName || 'default'}`);
await generateService({
...cfg,
requestLibPath: cfg.requestLibPath || "import { request } from '@umijs/max'",
serversPath: cfg.serversPath || './src/services',
enumStyle: 'enum',
declareType: 'interface',
} as GenerateServiceProps);
api.logger.info('[openApi] 生成完成');
}
},
});
};
如何使用
在config.ts 里面 替换如下代码
plugins: [path.resolve(__dirname, 'plugins/openApi-plugin.ts')],
openAPI: [
{
schemaPath: `${SERVER_NAME_HOST}/api-doc/openapi.json/pc`,
projectName: 'sign',
namespace: 'sign',
},
],

浙公网安备 33010602011771号