前言
这个 swagger-typescript-api 工具还不错,可以根据 swagger 文档生成 TypeScript 请求代码
包含有类型注解,可惜文档不太详细,这里我记录一下使用方法,免得每次生成代码都要去搜索
官方文档: https://acacode.github.io/swagger-typescript-api/
命令
基础用法
npx swagger-typescript-api generate -p http://localhost:5000/swagger/v1/swagger.json -o src/api -n index.ts如果是用 bun,则把 npx 替换为 bunx
我测试之后发现使用最基础的这个命令,把全部接口都放在一个文件反而最好,其他的比如 --modular 模块化参数,经常会导致生成的代码报错。
可用命令行参数一览
(整理自官方文档和 Fig.io)
-v,--version输出当前工具版本-p,--path <路径或 URL>指定 Swagger/OpenAPI 文档的位置(本地路径或网络 URL)-o,--output <目录路径>输出生成文件的目录(默认./)-n,--name <文件名>指定输出 TypeScript API 文件名(默认Api.ts)-t,--templates <模板路径>使用自定义 EJS 模板渲染生成逻辑-d,--default-as-success将"default"响应状态码也视为成功响应(一些 Swagger 用default),默认false-r,--responses生成额外的请求响应信息,包括出错类型的 typings--union-enums将所有枚举生成成 TypeScript 联合类型(T1 | T2 | TN)--add-readonly为生成的属性添加readonly修饰--route-types生成 API 路由相关的类型定义(如参数类型等)--client/--no-client是否生成 API 调用类(默认--client),执行--no-client则只生成类型/数据层--enum-names-as-values使用x-enumNames的值作为 enum 值,而不仅是 key(默认false)--extract-request-params/--extract-request-body/--extract-response-body/--extract-response-error将请求参数、请求体、响应体或错误响应提取成独立的数据契约类型--modular将 http client、数据契约、路由等代码拆分成多个文件(模块化)--js生成 JavaScript 模块和对应的.d.ts声明文件--module-name-index <索引>在模块化生成时决定从路径的哪个部分做索引分组--module-name-first-tag根据 API 的第一个 Tag 划分模块- 网络设置:
--disableStrictSSL(禁用严格 SSL 验证)--disableProxy(禁用代理) - HTTP 客户端选择:
--axios:生成以 axios 为底层客户端的请求代码 其他默认使用 fetch 或抽象 --unwrap-response-data自动拆解响应中的data字段,直接返回内部数据--disable-throw-on-error遇到response.ok !== true(HTTP 错误)时不抛异常(默认false)--single-http-client生成 API 类时支持传入单一的 HTTP client 实例(默认false)- 输出控制:
--silent:只输出错误信息,其它静默 - 类型生成配置:
--default-response <Type>:响应 schema 为空时的默认类型--type-prefix <前缀>/--type-suffix <后缀>:自定义数据模型名称前后缀 - 其他选项:
--clean-output:清理输出目录(注意会删除旧文件)--api-class-name <类名>:指定生成的 API 类名称--patch:修正 Swagger 源定义中的一些小错误--debug:输出额外调试信息--another-array-type:生成Array<Type>形式数组而非Type[](默认false)--sort-types:对字段和类型排序(默认false)--extract-enums:将所有枚举从 inline interface 中提取为独立的 TSenum - 帮助命令:
--help,-h:列出所有命令帮助信息
在 Next.js 里使用例子
以生成 StarBlog 的 API 接口为例
在 Next.js 项目中的目录结构是这样的:
其中 photo.ts 和 blog.ts 是生成的
lib
├─ api
│ └─ starblog
│ ├─ photo.ts
│ ├─ client.ts
│ └─ blog.ts
└─ source.ts这里需要创建一个 client.ts 方便使用,代码
import { Api as BlogApi } from './blog';
import { Api as PhotoApi } from './photo';
// 直接导出类型
export type { Post, Photo, FeaturedPost, PostListApiResponse, PostApiResponsePaged } from './blog';
export type { PhotoApiResponsePaged } from './photo';
/**
* 获取API基础URL
* @param baseUrl 可选的基础URL
* @returns 最终的API基础URL
*/
function getApiBaseUrl(baseUrl?: string): string {
// 在服务端环境中,优先使用服务端API URL
return typeof window === 'undefined'
? (process.env.API_BASE_URL || baseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:5000')
: (baseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:5000');
}
/**
* 创建博客API客户端
* @param baseUrl 可选的基础URL
* @returns 博客API实例
*/
export function createBlogApi(baseUrl?: string): BlogApi<unknown> {
return new BlogApi({
baseUrl: getApiBaseUrl(baseUrl),
customFetch: fetch,
});
}
/**
* 创建照片API客户端
* @param baseUrl 可选的基础URL
* @returns 照片API实例
*/
export function createPhotoApi(baseUrl?: string): PhotoApi<unknown> {
return new PhotoApi({
baseUrl: getApiBaseUrl(baseUrl),
customFetch: fetch,
});
}
// 为了向后兼容,保留原有的函数名
export const createStarBlogApiClient = createBlogApi;在页面里请求
import {createBlogApi, createPhotoApi, Post, Photo} from '@/lib/api/starblog/client';
/**
* 获取推荐博客文章
*/
async function getFeaturedPosts(): Promise<Post[]> {
try {
const blogApi = createBlogApi();
const response = await blogApi.api.blogFeaturedList();
if (response.data?.successful && response.data?.data) {
return response.data.data;
}
return [];
} catch (error) {
console.error('获取推荐文章失败:', error);
return [];
}
}
/**
* 获取摄影作品
*/
async function getPhotos(): Promise<Photo[]> {
try {
const photoApi = createPhotoApi();
console.log('正在获取摄影作品,API基础URL:', process.env.NEXT_PUBLIC_API_BASE_URL);
const response = await photoApi.api.photoList({page: 1, pageSize: 8});
console.log('摄影作品API响应:', response.data);
if (response.data?.successful && response.data?.data) {
console.log('获取到的摄影作品数量:', response.data.data.length);
response.data.data.forEach((photo, index) => {
console.log(`摄影作品 ${index + 1}:`, {
id: photo.id,
title: photo.title,
filePath: photo.filePath,
fullUrl: `${process.env.NEXT_PUBLIC_API_BASE_URL}/media/photography/${photo.filePath}`
});
});
return response.data.data;
}
console.warn('摄影作品API响应不成功或无数据');
return [];
} catch (error) {
console.error('获取摄影作品失败:', error);
return [];
}
}
export default async function HomePage() {
// 在服务端并行获取数据
const [posts, photos] = await Promise.all([
getPosts(),
getPhotos()
]);
return (
<div>
<BlogPosts
posts={posts}
baseUrl={process.env.NEXT_PUBLIC_API_BASE_URL || ''}
/>
<PhotoGallery
photos={photos}
baseUrl={process.env.NEXT_PUBLIC_API_BASE_URL || ''}
/>
</div>
)
}搞定