跳到主要内容

swagger-typescript-api

swagger-typescript-api 是一个专为 TypeScript 设计的工具,支持从 Swagger/OpenAPI 文档生成 TypeScript 类型定义和 API 客户端代码,支持按 OpenAPI tags 自动分割生成多个文件

工具说明

核心特性

  • 按 tags 分割:支持 --module-firstmodular: true,按 OpenAPI tags 组织文件
  • 灵活配置:可以只生成类型,也可以生成 API 客户端代码
  • 专为 TypeScript 设计:支持 Axios/Fetch,对前端开发友好
  • 支持 Swagger 2.0:原生支持 Swagger 2.0,无需转换

适用场景

  • 需要按 tags 分割生成多个文件
  • 希望生成轻量级的类型定义和 API 封装
  • 需要灵活的配置选项

安装

npm install -D swagger-typescript-api

使用方式

后端 Makefile 配置

FRONT_TYPES_DIR ?= ./frontend-types

generate-front-types-swagger-api: swagger ## 生成前端TypeScript类型定义(按tags分割,使用swagger-typescript-api)
@echo "generating frontend types to $(FRONT_TYPES_DIR) using swagger-typescript-api..."
@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)/$(FRONT_TYPES_DIR):/output \
node:20-alpine sh -c "\
npm install -g swagger-typescript-api && \
swagger-typescript-api generate \
--path /swagger.yaml \
--output /output \
--modular \
--module-name-first-tag"
@echo "Frontend types generated to $(FRONT_TYPES_DIR) (split by tags)"
@echo "Note: API files are split by tags, but data-contracts.ts contains all types"

关键配置说明

  • node:20-alpine:需要 Node.js 20+ 版本(工具依赖需要)
  • generate:必须指定 generate 命令
  • --modular:启用模块化输出
  • --module-name-first-tag:按第一个 tag 分割生成多个 API 文件
  • 注意:使用 --no-client 时不会按 tags 分割,只生成单个 data-contracts.ts 文件

配置文件方式

创建 swagger-typescript-api.config.ts

import { GenerateApiParams } from 'swagger-typescript-api';

export default {
path: './docs/swagger.yaml',
output: './frontend-types',
modular: true, // 按 tags 分割生成多个文件
moduleNameFirstTag: true, // 按第一个 tag 分割(与 --module-name-first-tag 对应)
httpClientType: 'axios', // 或 'fetch'
} as GenerateApiParams;

基础配置说明

  • modular:启用模块化输出,按 tags 分割生成多个文件
  • moduleNameFirstTag:按第一个 tag 分割生成多个 API 文件
  • httpClientType:选择 HTTP 客户端类型(axiosfetch

更多配置选项请参考下面的"配置文件完整选项"部分。

生成结果

会根据 OpenAPI 的 tags 生成多个 API 文件,但类型定义文件不分割:

frontend-types/
├── UserApi.ts # 用户相关 API(按 tag 分割)
├── AuthApi.ts # 认证相关 API(按 tag 分割)
├── RoleApi.ts # 角色相关 API(按 tag 分割)
├── PermissionApi.ts # 权限相关 API(按 tag 分割)
├── MenuApi.ts # 菜单相关 API(按 tag 分割)
├── OrderApi.ts # 订单相关 API(按 tag 分割)
├── ProductApi.ts # 产品相关 API(按 tag 分割)
├── data-contracts.ts # 所有类型定义(不分割,包含所有类型)
└── http-client.ts # HTTP 客户端

文件分割说明

  • API 文件:按 OpenAPI tags 自动分割生成多个文件,每个 tag 对应一个文件
  • 类型定义文件data-contracts.ts 包含所有类型定义,不按 tags 分割
  • HTTP 客户端http-client.ts 提供基础的 HTTP 请求封装

生成文件示例

UserApi.ts(按 tag 分割的 API 文件):

import {
UserInfoResponse,
UserListResponse,
CreateUserRequest,
UpdateUserRequest,
// ... 其他类型
} from "./data-contracts";
import { ContentType, HttpClient, RequestParams } from "./http-client";

export class UserApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* @description 获取用户信息
* @tags User
*/
getUserInfo = (params: RequestParams = {}) =>
this.request<UserInfoResponse>({
path: `/api/v1/users/{id}`,
method: "GET",
...params,
});

/**
* @description 获取用户列表
* @tags User
*/
getUserList = (params: RequestParams = {}) =>
this.request<UserListResponse>({
path: `/api/v1/users`,
method: "GET",
...params,
});

// ... 其他 API 方法
}

data-contracts.ts(所有类型定义):

export interface UserInfo {
id: number;
username: string;
email: string;
role: string;
createdAt: string;
}

export interface UserInfoResponse {
code: number;
msg: string;
data: UserInfo;
}

export interface UserListResponse {
code: number;
msg: string;
data: {
list: UserInfo[];
total: number;
};
}

export interface CreateUserRequest {
username: string;
email: string;
password: string;
role: string;
}

export interface LoginRequest {
username: string;
password: string;
}

export interface LoginResponse {
code: number;
msg: string;
data: {
token: string;
userId: number;
expireAt: number;
};
}

前端集成

复制生成的代码

将后端项目的 frontend-types/ 目录复制到前端项目的 src/api/{service-name}-types/ 目录。

使用生成的类型

import type { 
UserInfoResponse,
LoginRequest,
LoginResponse
} from '@/api/{service-name}-types/data-contracts';

// 自行封装 API 调用
import axios from '@/api/interceptor'; // 使用项目配置的 axios 实例

export async function getUserInfo(userId: number) {
const response = await axios.get<UserInfoResponse>(`/api/v1/users/${userId}`);
return response.data.data;
}

export async function login(request: LoginRequest) {
const response = await axios.post<LoginResponse>('/api/v1/auth/login', request);
return response.data;
}

使用生成的 API 类

import { UserApi } from '@/api/{service-name}-types/UserApi';
import { AuthApi } from '@/api/{service-name}-types/AuthApi';
import axios from '@/api/interceptor'; // 使用项目配置的 axios 实例

// 创建 API 实例
const userApi = new UserApi({
baseURL: '/api',
securityWorker: () => ({}), // 拦截器已处理认证
}, axios);

const authApi = new AuthApi({
baseURL: '/api',
securityWorker: () => ({}),
}, axios);

// 使用生成的 API
const userInfo = await userApi.getUserInfo({ path: { id: 123 } });
const loginResult = await authApi.login({ body: { username: 'admin', password: '123456' } });

配置选项

命令行参数

swagger-typescript-api generate \
--path swagger.yaml \
--output ./output \
--modular \ # 启用模块化输出
--module-name-first-tag \ # 按第一个 tag 分割生成多个 API 文件(必须与 --modular 一起使用)
--no-client \ # 不生成客户端代码,只生成类型(注意:使用此选项时不会按 tags 分割)
--axios \ # 使用 Axios(默认)
--fetch \ # 使用 Fetch
--templates ./templates # 自定义模板路径

关键参数说明

  • generate:必须指定 generate 命令
  • --modular:启用模块化输出,将 API 和类型分离
  • --module-name-first-tag:按第一个 tag 分割生成多个 API 文件,必须与 --modular 一起使用
  • --no-client:只生成类型定义,但不会按 tags 分割,只生成单个 data-contracts.ts 文件

配置文件完整选项

import { GenerateApiParams } from 'swagger-typescript-api';

export default {
path: './docs/swagger.yaml',
output: './frontend-types',

// 模块化配置
modular: true, // 启用模块化输出
moduleNameFirstTag: true, // 按第一个 tag 分割

// 客户端配置
noClient: false, // 是否生成客户端代码
httpClientType: 'axios', // 'axios' 或 'fetch'
singleHttpClient: false, // 是否使用单一 HTTP 客户端实例

// 类型提取配置
extractRequestParams: true, // 提取请求参数到 data contract
extractRequestBody: true, // 提取请求体到 data contract
extractResponseBody: true, // 提取响应体到 data contract
extractResponseError: false, // 提取错误响应类型

// 类型处理配置
enumNamesAsValues: true, // 生成枚举类型(而非联合类型)
generateUnionEnums: false, // 生成联合类型枚举
extractEnums: false, // 从内联内容提取枚举
typePrefix: 'Api', // 类型名称前缀
typeSuffix: '', // 类型名称后缀
defaultResponse: 'void', // 空响应的默认类型

// 代码生成配置
sortTypes: true, // 排序类型
sortRoutes: true, // 排序路由
routeTypes: false, // 生成路由类型定义
toJS: false, // 生成 JavaScript 文件(带 .d.ts)

// 模板和格式化
templates: './templates', // 自定义模板目录
cleanOutput: true, // 生成前清理输出目录
prettier: { // Prettier 配置
enable: true,
config: {
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
},
},
} as GenerateApiParams;

注意:使用配置文件时,noClient: true 会导致不按 tags 分割,只生成单个 data-contracts.ts 文件。

高级功能

类型系统优化

枚举类型处理

通过 enumNamesAsValues: true 可以生成 TypeScript 枚举类型:

// 生成的枚举类型
export enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest',
}

// 使用枚举
const role: UserRole = UserRole.Admin;

类型命名空间

使用 typePrefix 可以避免类型命名冲突:

// 配置 typePrefix: 'Api'
export interface ApiUserInfo {
id: number;
username: string;
}

export interface ApiUserListResponse {
code: number;
data: ApiUserInfo[];
}

泛型支持

工具自动处理泛型响应类型:

export interface ApiResponse<T> {
code: number;
msg: string;
data: T;
}

export type UserInfoResponse = ApiResponse<UserInfo>;
export type UserListResponse = ApiResponse<UserInfo[]>;

自定义模板

通过提供 EJS 模板可以完全控制生成的代码结构。创建模板目录 templates/

// templates/api.ejs
import { <%- typesName %> } from '../data-contracts'
import { httpClient } from '../http-client'

export class <%- className %> {
<% endpoints.forEach(endpoint => { %>
static async <%- endpoint.name %>(
<% if (endpoint.hasParams) { %>params: <%- endpoint.paramsType %>,<% } %>
): Promise<<%- endpoint.responseType %>> {
return httpClient.<%- endpoint.method %>(
'<%- endpoint.path %>',
<% if (endpoint.hasBody) { %>params.body<% } else { %>params<% } %>
)
}
<% }) %>
}

在配置文件中指定模板路径:

export default {
path: './docs/swagger.yaml',
output: './frontend-types',
templates: './templates',
// ... 其他配置
} as GenerateApiParams;

性能优化

对于大型 Swagger 文档,可以采用以下优化策略:

  1. 使用路由类型:启用 routeTypes: true 可以减少生成的文件数量
  2. 排除不需要的端点:在 Swagger 文档中标记不需要生成的端点
  3. 增量生成:只生成变更的部分(需要自定义脚本)
  4. 并行处理:对于多个服务,可以并行生成

最佳实践

后端 Swagger 标签规范

为了生成清晰的多文件结构,后端应该规范使用 @Tags 注解:

// @Tags 用户 API
// @Summary 用户信息
// @Router /api/v1/user/info [get]
func (u *UserController) GetUserInfo(ctx *gin.Context) {
// ...
}

// @Tags 权限管理 API
// @Summary 角色列表
// @Router /api/v1/authorization/role/list [get]
func (r *RoleController) GetRoleList(ctx *gin.Context) {
// ...
}

类型定义文件不分割

重要限制swagger-typescript-api 的类型定义文件 data-contracts.ts 不会按 tags 分割,所有类型都包含在一个文件中。只有 API 文件会按 tags 分割。

如果只需要类型定义,可以使用 --no-client 选项,但这样也不会按 tags 分割:

swagger-typescript-api generate \
--path swagger.yaml \
--output ./frontend-types \
--modular \
--module-name-first-tag \
--no-client

结果:只生成单个 data-contracts.ts 文件,不生成 API 文件。

类型导入规范

由于类型定义都在 data-contracts.ts 文件中,直接从该文件导入:

// ✅ 推荐:从 data-contracts.ts 导入需要的类型
import type {
UserInfoResponse,
LoginRequest,
LoginResponse
} from '@/api/{service-name}-types/data-contracts';

// ✅ 推荐:按需导入,避免导入过多类型
import type { UserInfoResponse } from '@/api/{service-name}-types/data-contracts';

代码格式化

启用 Prettier 格式化生成的代码,保持代码风格一致:

export default {
// ... 其他配置
prettier: {
enable: true,
config: {
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
printWidth: 100,
},
},
} as GenerateApiParams;

类型提取策略

合理使用类型提取选项,提高代码复用性:

export default {
extractRequestParams: true, // 提取请求参数,便于复用
extractRequestBody: true, // 提取请求体类型
extractResponseBody: true, // 提取响应体类型
} as GenerateApiParams;

这样可以将请求/响应类型提取到 data-contracts.ts,便于在不同 API 之间复用。

与构建工具集成

在 CI/CD 流程中集成代码生成:

# .github/workflows/generate-api.yml
name: Generate API Types

on:
push:
paths:
- 'docs/swagger.yaml'

jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
- run: npm install -g swagger-typescript-api
- run: swagger-typescript-api generate --path docs/swagger.yaml --output ./frontend-types --modular --module-name-first-tag
- run: |
# 复制到前端项目或提交到仓库

优势

  • 按 tags 分割 API 文件:支持按 OpenAPI tags 自动分割生成多个 API 文件,便于代码组织
  • 专为 TypeScript 设计:生成完整的 TypeScript 类型定义,对前端开发友好
  • 灵活配置:可以生成完整 API 客户端,也可以只生成类型定义
  • 支持 Swagger 2.0:原生支持 Swagger 2.0,无需转换为 OpenAPI 3.0
  • 类式 API 封装:生成的 API 以类的形式组织,便于使用和维护

限制

  • 类型定义不分割:所有类型定义都在 data-contracts.ts 文件中,不按 tags 分割
  • 使用 --no-client 时不分割:如果只生成类型定义(--no-client),不会按 tags 分割,只生成单个文件
  • Node.js 版本要求:需要 Node.js 20+ 版本

注意事项

文件分割规则

  • API 文件分割:基于 OpenAPI 的 tags,每个 tag 对应一个 API 文件
  • 类型定义不分割:所有类型定义都在 data-contracts.ts 文件中,不按 tags 分割
  • 确保后端 API 文档中正确使用 @Tags 注解

标签命名规范

为了生成清晰的文件名,建议后端使用英文 tags:

// ✅ 推荐:使用英文 tags
// @Tags User
// @Summary 用户信息
// @Router /api/v1/users/{id} [get]
func (u *UserController) GetUserInfo(ctx *gin.Context) {
// ...
}

// ❌ 不推荐:使用中文 tags(生成的文件名也会是中文)
// @Tags 用户 API

如果使用中文 tags,生成的文件名也会是中文(如 用户Api.ts权限管理Api.ts)。这在 TypeScript 中是可以正常使用的,但需要注意:

  • 文件名包含中文,某些工具可能不支持
  • 建议后端使用英文 tags,或配置文件名转换规则

Node.js 版本要求

swagger-typescript-api 需要 Node.js 20+ 版本,使用 Docker 时需要使用 node:20-alpine 或更高版本。

命令格式

必须使用 generate 命令:

# ✅ 正确
swagger-typescript-api generate --path swagger.yaml --output ./output

# ❌ 错误(缺少 generate 命令)
swagger-typescript-api --path swagger.yaml --output ./output

常见问题

生成的类型名称不符合预期

可以通过 typePrefixtypeSuffix 调整类型命名:

export default {
typePrefix: 'Api', // 所有类型添加 Api 前缀
typeSuffix: 'Type', // 所有类型添加 Type 后缀
} as GenerateApiParams;

枚举类型生成问题

如果希望生成枚举类型而不是联合类型:

export default {
enumNamesAsValues: true, // 生成枚举类型
// 或
generateUnionEnums: true, // 生成联合类型
} as GenerateApiParams;

大型 Swagger 文档生成缓慢

可以采取以下优化措施:

  • 使用 routeTypes: true 减少生成文件数量
  • 启用 cleanOutput: false 避免清理大目录
  • 考虑拆分 Swagger 文档,按服务分别生成

自定义 HTTP 客户端

生成的代码默认使用内置的 HTTP 客户端,如果需要使用项目的 axios 实例:

import { UserApi } from '@/api/{service-name}-types/UserApi';
import axios from '@/api/interceptor'; // 项目的 axios 实例

const userApi = new UserApi({
baseURL: '/api',
securityWorker: () => ({}),
}, axios); // 传入项目的 axios 实例

参考