跳到主要内容

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-apiorder-service-api 等。

常见问题

Swagger 版本兼容swaggo 生成的是 Swagger 2.0,OpenAPI Generator 会自动转换。

参数格式:GET 请求参数是独立的,不是对象形式,如 apiV1UserListGet(limit, page, uid, username)

响应格式:响应数据在 response.data.data 中(经过拦截器处理后)。

向后兼容:如果项目已有手动封装的 API,可以保持原有接口不变,内部使用生成的 API,实现渐进式迁移。

参考