OpenAPI Generator
通过 OpenAPI Generator 从后端 Swagger 文档自动生成 TypeScript 前端 API 客户端代码,减少手动编写 API 调用代码的工作量。
工具链
swaggo/swag- Go 项目生成 Swagger/OpenAPI 文档openapi-generator-cli- 从 OpenAPI 规范生成 TypeScript 客户端- Docker 容器运行,避免本地 Java/Node.js 依赖
实现步骤
后端 Swagger 文档生成
使用 swaggo/swag 生成 Swagger YAML 文档:
make swagger
# 输出: docs/swagger.yaml
配置生成参数
在项目根目录创建 .openapi-generator-config.json,该配置文件同时用于完整 API 客户端生成和仅类型定义生成:
{
"withSeparateModelsAndApi": true,
"modelPackage": "models",
"apiPackage": "apis",
"allowUnicodeldentifiers": true,
"ensureUniqueParams": true,
"enumNameSuffix": "v4-compat",
"enumPropertyNaming": "PascalCase",
"legacyDiscriminatorBehavior": true,
"modelPropertyNaming": "original",
"npmVersion": "v21.6.0",
"sortModelPropertiesByRequiredFlag": true,
"sortParamsByRequiredFlag": true,
"supportsES6": true,
"withInterfaces": true,
"useSingleRequestParameter": false
}
配置参数说明:
withSeparateModelsAndApi: 将模型和 API 接口分离到不同的目录(models/和apis/),便于代码组织modelPackage: 模型类的包名/目录名,生成的模型文件将放在此目录下apiPackage: API 接口类的包名/目录名,生成的 API 文件将放在此目录下allowUnicodeldentifiers: 允许在标识符中使用 Unicode 字符,支持中文等特殊字符ensureUniqueParams: 确保参数名称唯一,避免参数名冲突enumNameSuffix: 枚举名称后缀,用于兼容不同版本的生成器enumPropertyNaming: 枚举属性命名风格,PascalCase表示首字母大写的驼峰命名(如:UserStatus)legacyDiscriminatorBehavior: 使用传统的 discriminator 行为,保持向后兼容modelPropertyNaming: 模型属性命名风格,original表示保持原始命名,不进行转换npmVersion: 生成的package.json中的 npm 版本号sortModelPropertiesByRequiredFlag: 按 required 标志对模型属性进行排序,必需属性在前sortParamsByRequiredFlag: 按 required 标志对参数进行排序,必需参数在前supportsES6: 生成 ES6+ 语法的代码,使用现代 JavaScript 特性withInterfaces: 生成 TypeScript 接口定义,提供更好的类型支持useSingleRequestParameter: 是否使用单一请求参数对象,false表示使用多个独立参数
生成前端 API 代码
生成完整 API 客户端(包含类型和方法)
在 Makefile 中添加生成命令:
FRONT_API_DIR ?= ./frontend-api-client
generate-front-api: swagger ## 生成前端TypeScript API客户端代码
@echo "generating frontend API client to $(FRONT_API_DIR)..."
@mkdir -p $(FRONT_API_DIR)
docker run --rm \
-v $(PWD)/docs/swagger.yaml:/swagger.yaml:ro \
-v $(PWD)/.openapi-generator-config.json:/config.json:ro \
-v $(PWD)/$(FRONT_API_DIR):/output \
openapitools/openapi-generator-cli:v7.18.0 generate \
-g typescript-axios \
--skip-validate-spec \
-c /config.json \
-i /swagger.yaml \
-o /output
@echo "Frontend API client generated to $(FRONT_API_DIR)"
仅生成类型定义(按 schema 分割)
使用 --global-property models 选项可以只生成模型文件:
FRONT_TYPES_DIR ?= ./account-types
generate-front-types: swagger ## 生成前端TypeScript类型定义(按schema分割,仅类型)
@echo "generating frontend types to $(FRONT_TYPES_DIR)..."
@mkdir -p $(FRONT_TYPES_DIR)
@rm -rf $(FRONT_TYPES_DIR)/* 2>/dev/null || true
docker run --rm \
-v $(PWD)/docs/swagger.yaml:/swagger.yaml:ro \
-v $(PWD)/.openapi-generator-config.json:/config.json:ro \
-v $(PWD)/$(FRONT_TYPES_DIR):/output \
openapitools/openapi-generator-cli:v7.18.0 generate \
-g typescript-axios \
--skip-validate-spec \
--global-property models \
-c /config.json \
-i /swagger.yaml \
-o /output
@echo "Frontend types generated to $(FRONT_TYPES_DIR)/models (types only, split by schema)"
关键配置说明:
--global-property models:只生成模型文件,不生成 API 客户端代码和支持文件-c /config.json:使用配置文件读取生成参数,配置集中管理,便于维护- 配置文件
.openapi-generator-config.json与完整 API 客户端生成共用,保持配置一致性 - 直接生成到挂载目录
/output,无需临时目录和复制步骤 - 生成的文件按 schema 分割,每个 schema 对应一个文件
- 生成结果包含
models/目录(类型文件)和docs/目录(文档文件,可选删除)
执行:
make generate-front-types
仅类型定义的使用方式:
- 将生成的
account-types/models/目录复制到前端项目的src/api/{service-name}-types/models/目录 - 在前端代码中导入类型定义,自行封装 API 调用函数
- 示例:
// 导入类型定义
import type { AuthorizationLoginRequest, AuthorizationLoginResponse } from '@/api/account-types/models/authorization-login-request';
// 自行封装 API 调用
import axios from '@/api/interceptor'; // 使用项目配置的 axios 实例
export function login(data: AuthorizationLoginRequest) {
return axios.post<AuthorizationLoginResponse>('/api/v1/login', data);
}
前端集成
复制生成的代码
将后端项目的 frontend-api-client/ 目录复制到前端项目的 src/api/{service-name}-api/ 目录。
创建 API 客户端实例
创建 src/api/{service-name}-api/client.ts:
import axios from 'axios';
import { APIApi } from './api';
import { Configuration } from './configuration';
import config from '@/config';
const apiConfig = new Configuration({
basePath: config.API_BASE_URL || '/api',
accessToken: () => '', // 拦截器已处理 token
});
export const apiClient = new APIApi(apiConfig, undefined, axios);
在组件或 Store 中使用
// Vue 组件
import { apiClient } from '@/api/{service-name}-api/client';
export default {
async mounted() {
try {
const response = await apiClient.apiV1UserInfoGet(123);
const userInfo = response.data.data;
console.log(userInfo);
} catch (error) {
console.error('获取用户信息失败:', error);
}
}
}
// Pinia Store
import { defineStore } from 'pinia';
import { apiClient } from '@/api/{service-name}-api/client';
export const useUserStore = defineStore('user', {
actions: {
async fetchUserInfo(uid: number) {
const response = await apiClient.apiV1UserInfoGet(uid);
return response.data.data;
}
}
});
注意事项
- 传入项目的 axios 实例(第三个参数),确保使用已配置拦截器的实例
- 不要在 API 文件中导入
interceptor.ts,因为main.ts已导入 - 生成的代码可以直接使用,无需额外封装
- 提供完整的 TypeScript 类型定义,IDE 有完整的类型提示
优势
后端驱动生成
生成逻辑在后端代码库中,前端只需复制生成的代码,避免跨仓库路径依赖问题。
Docker 容器化
使用 Docker 运行 openapi-generator-cli,无需本地安装 Java/Node.js,环境一致性好。
类型安全
生成的代码提供完整的 TypeScript 类型定义,IDE 自动补全和类型检查,前后端接口定义自动同步。
生成代码结构
src/api/{service-name}-api/
├── apis/ # API 接口类
│ ├── apiapi.ts # 主 API 类
│ └── ...
├── models/ # TypeScript 类型定义
│ ├── {model-name}.ts
│ └── ...
├── base.ts # 基础类
├── configuration.ts # 配置类
├── common.ts # 通用工具
└── index.ts # 统一导出
{service-name} 需要根据实际服务名称替换,如 user-service-api、order-service-api 等。
常见问题
Swagger 版本兼容:swaggo 生成的是 Swagger 2.0,OpenAPI Generator 会自动转换。
参数格式:GET 请求参数是独立的,不是对象形式,如 apiV1UserListGet(limit, page, uid, username)。
响应格式:响应数据在 response.data.data 中(经过拦截器处理后)。
向后兼容:如果项目已有手动封装的 API,可以保持原有接口不变,内部使用生成的 API,实现渐进式迁移。