Skip to content

Latest commit

 

History

History
683 lines (519 loc) · 14.2 KB

File metadata and controls

683 lines (519 loc) · 14.2 KB

OpenAPI TypeScript CLI 使用文档

一个优雅的从 OpenAPI/Swagger 规范生成 TypeScript API 请求层代码的工具

📖 目录


快速开始

# 1. 安装工具
npm install -g openapi-typescript-cli

# 2. 生成代码(从本地文件)
openapi-typescript-cli -f ./openapi.json -n api

# 3. 生成代码(从远程 URL)
openapi-typescript-cli -u http://localhost:8080/v3/api-docs -n api

# 4. 使用中间件自定义命名
openapi-typescript-cli -f ./openapi.json -n api -m ./middleware.js

安装

全局安装

npm install -g openapi-typescript-cli

本地安装

npm install --save-dev openapi-typescript-cli

然后在 package.json 中添加脚本:

{
  "scripts": {
    "generate:api": "openapi-typescript-cli -f ./openapi.json -n api"
  }
}

基本使用

从本地文件生成

openapi-typescript-cli -f ./openapi.json -n api

生成的文件:

  • api.ts - API 请求函数
  • api.d.ts - TypeScript 类型定义
  • request.js - Axios 封装(首次生成)
  • middleware.example.js - 中间件示例(首次生成)

从远程 URL 生成

openapi-typescript-cli -u http://localhost:8080/v3/api-docs -n api

支持的 URL 格式:

  • Spring Boot: http://localhost:8080/v3/api-docs
  • Swagger UI: http://localhost:8080/swagger.json
  • 其他符合 OpenAPI 3.0 规范的 JSON 端点

命令参数

参数 简写 说明 必填 默认值
--apifile -f OpenAPI JSON 文件路径 否* -
--url -u OpenAPI JSON 文件 URL 地址 否* -
--name -n 输出文件名称 index
--middleware -m 中间件文件路径 -

注意: --apifile--url 至少需要提供一个。

示例

# 使用本地文件
openapi-typescript-cli -f ./openapi.json -n api

# 使用远程 URL
openapi-typescript-cli -u http://localhost:8080/v3/api-docs -n api

# 指定输出文件名
openapi-typescript-cli -f ./openapi.json -n petstore

# 使用中间件
openapi-typescript-cli -f ./openapi.json -n api -m ./middleware.js

中间件配置

中间件用于自定义生成的模块名和函数名。

创建中间件文件

创建 middleware.js

module.exports = function ({operationId, description, path, method, tag, operation}) {
  return {
    moduleName: tag,        // 模块名,对应 export let xxx
    functionName: operationId || '',  // 函数名
  }
}

自定义命名示例

module.exports = function ({operationId, description, path, method, tag, operation}) {
  // 示例:从路径提取模块名
  // 路径:/api/v1/user/profile
  // 提取:user
  const pathParts = path.split('/').filter(p => p);
  const moduleName = pathParts[1] || tag; // 使用路径的第二段作为模块名
  
  // 示例:重命名函数
  let functionName = operationId;
  if (operationId === 'getUserById') {
    functionName = 'fetchUser';
  }
  
  return {
    moduleName: moduleName,
    functionName: functionName,
  }
}

使用中间件

openapi-typescript-cli -f ./openapi.json -n api -m ./middleware.js

生成的代码结构

类型定义文件 (api.d.ts)

// 从 components.schemas 生成的接口
export interface Pet {
  id?: number;
  name: string;           // 必需字段
  photoUrls: string[];    // 必需字段
  status?: 'available' | 'pending' | 'sold';  // Enum → Union Type
}

export interface Order {
  shipDate?: Date;        // date-time → Date
  status?: 'placed' | 'approved' | 'delivered';
}

// 查询参数类型
export interface QueryTypefindPetsByStatus {
  status?: 'available' | 'pending' | 'sold';
}

// 路径参数类型
export interface PathTypegetPetById {
  petId: number;  // 路径参数必需
}

API 代码文件 (api.ts)

import request from "./request"
import { AxiosRequestConfig } from 'axios'
import * as Type from './api.d'

export let pet = {
  /**
   * Find pet by ID.
   * Returns a single pet.
   * @tags pet
   */
  getPetById: async (
    param: Type.PathTypegetPetById, 
    opt: AxiosRequestConfig = {}
  ): Promise<Type.Pet> => await request({
    url: `/pet/${param.petId}`,  // 路径参数使用模板字符串
    method: 'get',
    ...opt,
  }),
  
  findPetsByStatus: async (
    param: Type.QueryTypefindPetsByStatus, 
    opt: AxiosRequestConfig = {}
  ): Promise<Type.Pet[]> => await request({
    url: '/pet/findByStatus',
    method: 'get',
    params: {status: param?.status,},
    ...opt,
  }),
}

Request 文件 (request.js)

// 这个文件在首次生成后不会被覆盖
// 你可以安全地自定义拦截器、错误处理等

import axios from 'axios'

const instance = axios.create({
  baseURL: '/',
  timeout: 10000,
});

// 请求拦截器
instance.interceptors.request.use((config) => {
  // 添加 token、loading 等
  return config;
});

// 响应拦截器
instance.interceptors.response.use(
  (res) => {
    // 处理响应数据
    return res.data;
  },
  (error) => {
    // 处理错误
    return Promise.reject(error);
  }
);

export default instance;

类型优化特性

工具会根据 OpenAPI 规范自动优化生成的类型:

1. Enum → Union Type

// OpenAPI
{
  "status": {
    "type": "string",
    "enum": ["available", "pending", "sold"]
  }
}

// 生成的 TypeScript
status?: 'available' | 'pending' | 'sold';

2. Required 字段

// OpenAPI
{
  "required": ["name", "photoUrls"],
  "properties": {
    "name": {"type": "string"},
    "photoUrls": {"type": "array"}
  }
}

// 生成的 TypeScript
export interface Pet {
  name: string;        // 必需(无 ?)
  photoUrls: string[]; // 必需(无 ?)
  id?: number;         // 可选(有 ?)
}

3. Format 处理

// OpenAPI
{
  "shipDate": {
    "type": "string",
    "format": "date-time"
  }
}

// 生成的 TypeScript
shipDate?: Date;  // date-time → Date

4. 路径参数与查询参数分离

// 路径参数类型(必需)
export interface PathTypegetPetById {
  petId: number;
}

// 查询参数类型(可选)
export interface QueryTypefindPetsByStatus {
  status?: 'available' | 'pending' | 'sold';
}

// 组合使用
updatePetWithForm: async (
  param: Type.PathTypeupdatePetWithForm & Type.QueryTypeupdatePetWithForm,
  ...
) => ...

5. 数组类型

// 生成正确的数组类型
Promise<Type.Pet[]>  // ✅ 正确
param: Type.User[]   // ✅ 正确

6. 索引签名类型

// OpenAPI additionalProperties
{
  "type": "object",
  "additionalProperties": {
    "type": "integer"
  }
}

// 生成的 TypeScript
Promise<{ [key: string]: number }>  // ✅ 正确,无 Type. 前缀

常见问题

Q1: 生成的代码有语法错误怎么办?

A: 如果生成的接口代码有语法错误,大概率是后端 OpenAPI 文档有语法错误。

常见问题:

  1. Schema 定义不完整 - 缺少 type 字段
  2. 参数定义错误 - in 字段缺失或错误
  3. 响应定义缺失 - 缺少 responses 定义
  4. 类型不匹配 - typeformat 不匹配

解决方法:

Q2: 生成的类型不正确?

A: 检查后端 OpenAPI 文档中的类型定义。

确保后端正确使用了:

  • @Schema 注解(Java Spring Boot)
  • required 字段
  • enum 定义
  • format 定义(date-time, int64 等)

Q3: 路径参数没有正确替换?

A: 检查后端路径参数定义。

确保 OpenAPI 文档中:

{
  "parameters": [
    {
      "name": "petId",
      "in": "path",      // ✅ 必须是 "path"
      "required": true,  // ✅ 路径参数必须 required
      "schema": {
        "type": "integer"
      }
    }
  ]
}

Q4: 生成的模块名不符合预期?

A: 使用中间件自定义。

创建 middleware.js 文件自定义模块名和函数名。

Q5: 如何更新生成的代码?

A: 直接重新运行生成命令。

生成的 request.js 不会被覆盖,其他文件会被覆盖。


⚠️ 重要提示:后端 OpenAPI 文档质量

问题根源分析

如果生成的接口代码有错误,99% 的情况是后端 OpenAPI 文档的语法错误或定义不完整。

工具本身只是忠实地将 OpenAPI 规范转换为 TypeScript 代码。如果后端文档有问题,生成的结果也会有问题。

常见后端文档错误

1. Schema 定义不完整

// ❌ 错误示例
{
  "properties": {
    "name": {}  // 缺少 type 字段
  }
}

// ✅ 正确示例
{
  "properties": {
    "name": {
      "type": "string"
    }
  }
}

2. 参数定义错误

// ❌ 错误示例
{
  "parameters": [
    {
      "name": "petId"
      // 缺少 in、schema 字段
    }
  ]
}

// ✅ 正确示例
{
  "parameters": [
    {
      "name": "petId",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer"
      }
    }
  ]
}

3. 响应定义缺失

// ❌ 错误示例
{
  "get": {
    "operationId": "getPet"
    // 缺少 responses 字段
  }
}

// ✅ 正确示例
{
  "get": {
    "operationId": "getPet",
    "responses": {
      "200": {
        "description": "successful operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Pet"
            }
          }
        }
      }
    }
  }
}

4. 类型与格式不匹配

// ❌ 错误示例
{
  "type": "string",
  "format": "int64"  // format 应该是 integer 的
}

// ✅ 正确示例
{
  "type": "integer",
  "format": "int64"
}

如何验证后端 OpenAPI 文档

方法 1: 使用 Swagger Editor

  1. 访问 https://editor.swagger.io/
  2. 将后端生成的 OpenAPI JSON 粘贴进去
  3. 查看是否有错误提示(红色标记)

方法 2: 使用命令行工具

# 安装 swagger-codegen-cli
npm install -g @openapitools/openapi-generator-cli

# 验证文档
openapi-generator-cli validate -i ./openapi.json

方法 3: 检查后端注解

Spring Boot 示例:

// ✅ 正确示例
@RestController
@RequestMapping("/api/pet")
public class PetController {
    
    @GetMapping("/{petId}")
    @Operation(summary = "Find pet by ID")
    @ApiResponse(
        responseCode = "200",
        description = "successful operation",
        content = @Content(
            mediaType = "application/json",
            schema = @Schema(implementation = Pet.class)
        )
    )
    public ResponseEntity<Pet> getPetById(
        @Parameter(description = "ID of pet to return", required = true)
        @PathVariable Integer petId
    ) {
        // ...
    }
}

常见注解错误:

// ❌ 错误:缺少 @Parameter 注解
@GetMapping("/{petId}")
public ResponseEntity<Pet> getPetById(@PathVariable Integer petId) {
    // ...
}

// ✅ 正确:完整的注解
@GetMapping("/{petId}")
@Operation(summary = "Find pet by ID")
@ApiResponse(responseCode = "200", ...)
public ResponseEntity<Pet> getPetById(
    @Parameter(description = "ID of pet", required = true)
    @PathVariable Integer petId
) {
    // ...
}

后端文档质量检查清单

在生成代码前,确保后端 OpenAPI 文档:

  • 所有 Schema 都有 type 字段
  • 所有参数都有 in 字段(path/query/header)
  • 所有操作都有 responses 定义
  • 路径参数 required: true
  • Enum 使用 enum 字段定义
  • 必需字段使用 required 数组
  • Date 类型使用 format: "date-time"
  • 使用 Swagger Editor 验证无错误

最佳实践

1. 后端规范建议

  • 使用完整的注解:确保所有接口都有完整的 Swagger/OpenAPI 注解
  • 定义 Schema:将所有实体类定义为 @Schema,并在 components.schemas 中引用
  • 明确类型:使用正确的 typeformat
  • 必需字段:正确使用 required 数组

2. 前端使用建议

  • 版本控制:将生成的代码纳入版本控制,但不要手动修改
  • 定期更新:后端接口变更后,及时重新生成代码
  • 自定义 Request:在 request.js 中添加统一的拦截器、错误处理
  • 使用中间件:通过中间件统一命名规范

3. 团队协作

  • 文档优先:后端先完善 OpenAPI 文档,再让前端生成代码
  • 验证流程:在生成代码前,使用 Swagger Editor 验证文档
  • 错误反馈:如果生成错误,先检查后端文档,再反馈给后端团队

4. 调试技巧

# 1. 先验证 OpenAPI 文档
# 在 Swagger Editor 中验证

# 2. 使用中间件调试
# 在 middleware.js 中添加 console.log 查看原始数据

# 3. 检查生成的代码
# 查看生成的文件,定位问题所在

# 4. 对比后端文档
# 检查对应接口的 OpenAPI 定义是否正确

技术支持


总结

  1. 工具是代码生成器,不是文档校验器

    • 如果生成的代码有错误,先检查后端 OpenAPI 文档
  2. 使用 Swagger Editor 验证文档

    • 这是验证 OpenAPI 文档最可靠的方法
  3. 与后端团队协作

    • 确保后端提供完整、正确的 OpenAPI 文档
  4. 定期更新生成

    • 后端接口变更后及时重新生成代码
  5. 善用中间件

    • 通过中间件统一命名规范和自定义逻辑

记住:生成工具的质量取决于输入文档的质量! 📝✅