问题场景
每次后端甩过来一个 Swagger JSON,你就得手动:
- 对着文档写
interface UserResponse { ... },几十个字段眼睛看花 - 手撸
getUserList()、createOrder()每个接口对应的请求函数 - 处理请求/响应拦截、错误码、loading 状态------每个接口重复一遍
项目一多,接口上百个,光维护类型定义就够喝一壶的。更坑的是:后端改了字段名你根本不知道 ,直到线上报 undefined。
原因分析
手写 API 层的三个痛点:
- 重复劳动:CRUD 接口结构高度相似,纯体力活
- 类型漂移:后端改了字段,前端类型不同步,全靠心电感应
- 缺乏约束 :没有人能保证你拼的 URL
user/list和实际路由一致
解决方案:LLM + 脚本 = 自动生成 API 层
第一步:拿到 Swagger/OpenAPI 文档
大多数后端框架都会暴露一个 /v3/api-docs 或者能导出 swagger.json:
bash
# 从本地 dev 服务拉取
curl http://localhost:8080/v3/api-docs > swagger.json
# 或者从后端仓库拿到导出的 JSON 文件
第二步:写一段 Prompt,喂给 LLM
写好一个固定 prompt 模板,把 swagger.json 作为上下文传入:
markdown
你是一个前端代码生成器。
根据以下 OpenAPI 3.0 文档,生成 TypeScript 类型定义 + 基于 axios 的请求函数。
要求:
1. 每个接口生成一个请求函数,路径参数自动拼接,query/body 自动推导类型
2. 响应包裹统一泛型包装:ApiResponse<T>
3. 按 tag 分组到不同文件(user.ts, order.ts, product.ts)
4. 导出一个总入口 api/index.ts
5. 不要用 any,所有字段严格推导
以下是文档:
第三步:用脚本批量执行
写个 Node 脚本一键完成:
ts
// scripts/generate-api.ts
import fs from 'fs'
import path from 'path'
import { execSync } from 'child_process'
const swagger = JSON.parse(fs.readFileSync('swagger.json', 'utf-8'))
const prompt = `你是一个前端代码生成器。...` // 上面的模板
// 把 prompt + swagger 拼接成一个临时文件
const content = `${prompt}\n\n```json\n${JSON.stringify(swagger, null, 2)}\n````
fs.writeFileSync('_temp_prompt.md', content)
// 调用 CLI 版 LLM(这里以 Claude Code 为例)
execSync(`claude -p "$(cat _temp_prompt.md)" -o src/api/`, { stdio: 'inherit' })
fs.unlinkSync('_temp_prompt.md')
console.log('✅ API 层生成完毕,请到 src/api/ 查看')
第四步:看效果
生成的代码长这样:
ts
// src/api/user.ts
import { request } from './request'
export interface GetUserListParams {
page: number
pageSize: number
role?: string
}
export interface UserItem {
id: number
name: string
email: string
avatar: string
createdAt: string
}
export function getUserList(params: GetUserListParams) {
return request.get<ApiResponse<UserItem[]>>('/api/users', { params })
}
export function createUser(data: { name: string; email: string; role: string }) {
return request.post<ApiResponse<UserItem>>('/api/users', data)
}
调用时:
ts
import { getUserList, createUser } from '@/api/user'
const { data } = await getUserList({ page: 1, pageSize: 20 })
// data.list 自动是 UserItem[],TS 完全推导 ✅
第五步:集成到 CI,杜绝类型漂移
在 package.json 加一条脚本:
json
{
"scripts": {
"generate:api": "tsx scripts/generate-api.ts"
}
}
每次后端更新文档,执行一次 pnpm generate:api,类型自动同步。
进阶:自定义生成规则
如果 LLM 生成的不够稳定,可以用 openapi-typescript 先生成 TS 类型,再用 LLM 补请求函数:
bash
npx openapi-typescript swagger.json -o src/api/schema.d.ts
然后让 LLM 基于 schema.d.ts 生成请求函数,这样类型部分 100% 精确,LLM 只负责写调用逻辑:
ts
// prompt 改成这样:
// 基于 src/api/schema.d.ts 中的类型定义,
// 为以下 paths 生成 axios 请求函数:
// - paths['/api/users'] 的 get 方法 → getUserList
// - paths['/api/users/{id}'] 的 get 方法 → getUserById
要点总结
| 做了什么 | 收益 |
|---|---|
| Swagger → TS 类型 | 不再手写 interface,0 遗漏 |
| LLM 生成请求函数 | 一个 prompt 生成上百个接口,2 分钟 vs 2 天 |
| CI 脚本自动化 | 后端改字段,本地 pnpm generate:api 即刻同步 |
| openapi-typescript 兜底类型 | 绕过 LLM "幻觉",类型 100% 准确 |
最佳实践:
- 一个项目用同一个 prompt 模板,生成风格一致
- 每次重新生成前 git commit 一次,不满意随时回滚
- 对生成的代码做 code review,跑一次 type-check
AI 不是替你写代码,是替你把"复制粘贴改字段名"这种脏活干了。Swagger → 类型安全 API 层,是这个场景里 ROI 最高的玩法。