12k
All articles

使用 OpenAPI 和 TypeScript 构建类型安全的 API 客户端

介绍如何使用 openapi-typescript、openapi-fetch 和 Orval 从 OpenAPI 规范生成 TypeScript 类型,构建类型安全的 API 客户端并消除运行时错误。

OpenReplay Team
OpenReplay Team
使用 OpenAPI 和 TypeScript 构建类型安全的 API 客户端

每个前端开发者都遇到过这种情况:从 API 获取数据,访问一个应该存在的字段,结果在运行时得到 undefined。TypeScript 本应防止这种情况。问题在于 response.json() 返回 any,因此编译器无法进行检查。本指南将向你展示如何正确解决这个问题——直接从 OpenAPI 规范生成类型,并使用它们在 TypeScript 中构建类型安全的 REST 客户端。

核心要点

  • 手动编写的 TypeScript 接口会与实际 API 产生偏差,造成虚假的安全感,最终导致运行时错误。
  • OpenAPI 代码生成直接从 API 规范生成类型,使前端和后端保持同步。
  • 使用 openapi-typescript 配合 openapi-fetch 实现轻量级的、基于 fetch 的设置,或使用 Orval 生成 TanStack Query hooks 和完整的客户端 SDK。
  • 在 CI 或构建脚本中自动化类型生成,确保类型永不过时。
  • 将生成的类型与 Zod 等运行时验证器配合使用,为关键端点提供编译时安全之外的额外保障。

为什么手动类型定义会失效

简单粗暴的修复方法是手写接口:

// ❌ 编译通过,但 TypeScript 只是在相信你
const data = (await response.json()) as User

这能给你自动补全,但一旦后端发生变化,它就变成了谎言。你的类型与现实产生偏差,又回到了运行时意外的老路上。

更好的方法:从真实来源——你的 OpenAPI 规范——生成类型。

OpenAPI TypeScript 代码生成工作流

典型的工作流如下:

  1. 从 OpenAPI 规范开始(YAML 或 JSON,托管或本地)
  2. 运行生成器生成 TypeScript 类型或完整客户端
  3. 将这些类型导入到应用程序代码中

这使你的前端自动与后端保持同步。当规范发生变化时,重新生成即可,TypeScript 会准确告诉你哪里出了问题。

选择你的方案:仅类型 vs 完整客户端 SDK

OpenAPI TypeScript 代码生成有两种主要策略,正确的选择取决于你的项目。

方案工具最适合
生成类型,自带 fetchopenapi-typescript + openapi-fetch轻量级、基于 fetch 的项目
生成完整客户端 SDKOrvalOpenAPI Generator希望使用现成 hooks 和客户端的团队

仅类型生成保持打包体积小,让你控制 HTTP 层。完整 SDK 生成节省连接时间,但会增加更多需要维护的生成代码。

OpenAPI 3.0 vs 3.1:大多数工具对 OpenAPI 3.0 支持良好。OpenAPI 3.1 的支持程度各不相同——在假设完全兼容之前,请查看生成器的文档。

方案 1:openapi-typescript 配合 openapi-fetch

这是最小运行时路径。从规范生成类型,然后使用 openapi-fetch 作为原生 Fetch API 的轻量级、完全类型化的包装器。

npx openapi-typescript ./openapi.yaml -o ./src/api/types.ts
npm install openapi-fetch
import createClient from "openapi-fetch"
import type { paths } from "./api/types"

const client = createClient<paths>({ baseUrl: "https://api.example.com" })

// 路径、参数和响应都经过类型检查
const { data, error } = await client.GET("/users/{id}", {
  params: { path: { id: 123 } },
})

// TypeScript 准确知道 `data` 的结构
console.log(data?.email)

路径拼写错误、参数类型错误和响应结构不匹配都会变成编译错误。最小的运行时开销,只有一个小型运行时依赖(openapi-fetch)。

方案 2:使用 Orval 生成完整客户端

Orval 生成类型化的 API 函数,更重要的是,可以直接从规范输出 TanStack Query hooks。当你希望数据获取逻辑自动处理时,这非常有用。

npm install orval --save-dev

配置 orval.config.ts 指向你的规范并选择输出模式(fetch、axios 或 react-query)。然后 Orval 会生成像 useGetUsers() 这样的函数,内置完整的类型安全。

这种方法会增加更多生成的代码,但对于大型 API,它能显著减少样板代码。

保持类型同步

只有持续重新生成,OpenAPI TypeScript 客户端生成的真正价值才能体现。将生成步骤添加到开发工作流中:

{
  "scripts": {
    "generate:api": "openapi-typescript ./openapi.yaml -o ./src/api/types.ts"
  }
}

在 CI 中运行,或在本地监听规范变化。一些团队提交生成的文件;另一些团队在每次构建时重新生成。两种方式都可行——只要让它自动化即可。

你仍需处理的问题

生成的类型通常只提供编译时安全。运行时验证需要额外的工具,如 Zod 或生成器插件。对于关键端点,将生成的类型与 Zod 配合使用,在运行时验证响应,在后端 bug 影响 UI 之前捕获它们。

总结

OpenAPI TypeScript 代码生成是你可以对前端代码库做出的最高杠杆率改进之一。选择 openapi-typescript 配合 openapi-fetch 实现轻量级设置,或者如果你想要生成的查询 hooks,选择 Orval。无论哪种方式,你都不再需要手写类型,不再猜测响应结构,让编译器做它本该做的工作。

常见问题

openapi-fetch 是否支持 OpenAPI 3.1 规范?

是的。openapi-typescript 同时支持 OpenAPI 3.0 和 3.1 规范,尽管某些 schema 特性可能需要使用你的特定生成器和规范进行测试。升级规范版本后,务必验证生成的输出。

我可以将 Zod 与生成的 OpenAPI 类型一起使用吗?

当然可以。生成的类型为你提供编译时安全,而 Zod 在运行时验证数据。你可以定义与生成类型相对应的 Zod schemas,并通过它们解析 API 响应。这能捕获编译器无法检测到的、后端返回意外数据的情况。

我应该将生成的类型文件提交到版本控制吗?

两种方法都可行。提交生成的文件使构建更快,并允许你在 pull request 中审查类型变化。在每次构建时重新生成确保类型始终是最新的,但会增加构建步骤依赖。选择适合你团队工作流和 CI 设置的方式。

对于 TypeScript 项目,Orval 与 OpenAPI Generator 相比如何?

Orval 专为 TypeScript 构建,只需最少配置即可输出 TanStack Query hooks、Axios 客户端或 fetch 函数。OpenAPI Generator 支持多种语言,但生成的 TypeScript 输出更冗长。对于专注前端的团队,Orval 通常需要更少的自定义即可获得简洁、惯用的代码。

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.