# 生成 SDK { #generating-sdks }
因为 **FastAPI** 基于 **OpenAPI** 规范,它的 API 可以用许多工具都能理解的标准格式来描述。
这让你可以轻松生成最新的**文档**、多语言的客户端库(**SDKs**),以及与代码保持同步的**测试**或**自动化工作流**。
本指南将带你为 FastAPI 后端生成一个 **TypeScript SDK**。
## 开源 SDK 生成器 { #open-source-sdk-generators }
一个功能多样的选择是 OpenAPI Generator,它支持**多种编程语言**,可以根据你的 OpenAPI 规范生成 SDK。
对于 **TypeScript 客户端**,Hey API 是为 TypeScript 生态打造的专用方案,提供优化的使用体验。
你还可以在 OpenAPI.Tools 上发现更多 SDK 生成器。
/// tip | 提示
FastAPI 会自动生成 **OpenAPI 3.1** 规范,因此你使用的任何工具都必须支持该版本。
///
## 来自 FastAPI 赞助商的 SDK 生成器 { #sdk-generators-from-fastapi-sponsors }
本节介绍的是由赞助 FastAPI 的公司提供的、具备**风险投资背景**或**公司支持**的方案。这些产品在高质量生成的 SDK 之上,提供了**更多特性**和**集成**。
通过 ✨ [**赞助 FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨,这些公司帮助确保框架及其**生态**保持健康并且**可持续**。
他们的赞助也体现了对 FastAPI **社区**(也就是你)的高度承诺,不仅关注提供**优秀的服务**,也支持一个**健壮且繁荣的框架**——FastAPI。🙇
例如,你可以尝试:
* Speakeasy
* Stainless
* liblab
其中一些方案也可能是开源的或提供免费层级,你可以不花钱就先试用。其他商业 SDK 生成器也可在网上找到。🤓
## 创建一个 TypeScript SDK { #create-a-typescript-sdk }
先从一个简单的 FastAPI 应用开始:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
请注意,这些*路径操作*使用 `Item` 和 `ResponseMessage` 模型来定义它们的请求载荷和响应载荷。
### API 文档 { #api-docs }
访问 `/docs` 时,你会看到有用于请求发送和响应接收数据的**模式**:
之所以能看到这些模式,是因为它们在应用中用模型声明了。
这些信息会包含在应用的 **OpenAPI 模式** 中,并显示在 API 文档里。
OpenAPI 中包含的这些模型信息就是用于**生成客户端代码**的基础。
### Hey API { #hey-api }
当我们有了带模型的 FastAPI 应用后,可以使用 Hey API 来生成 TypeScript 客户端。最快的方式是通过 npx:
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
这会在 `./src/client` 生成一个 TypeScript SDK。
你可以在其官网了解如何安装 `@hey-api/openapi-ts`,以及阅读生成结果的说明。
### 使用 SDK { #using-the-sdk }
现在你可以导入并使用客户端代码了。它可能是这样,并且你会发现方法有自动补全:
要发送的载荷也会有自动补全:
/// tip | 提示
请注意 `name` 和 `price` 的自动补全,它们是在 FastAPI 应用中的 `Item` 模型里定义的。
///
你发送的数据如果不符合要求,会在编辑器中显示内联错误:
响应对象同样有自动补全:
## 带有标签的 FastAPI 应用 { #fastapi-app-with-tags }
很多情况下,你的 FastAPI 应用会更大,你可能会用标签来划分不同组的*路径操作*。
例如,你可以有一个 **items** 相关的部分和另一个 **users** 相关的部分,它们可以用标签来分隔:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### 生成带标签的 TypeScript 客户端 { #generate-a-typescript-client-with-tags }
如果你为使用了标签的 FastAPI 应用生成客户端,通常也会根据标签来拆分客户端代码。
这样你就可以在客户端代码中把内容正确地组织和分组:
在这个例子中,你会有:
* `ItemsService`
* `UsersService`
### 客户端方法名 { #client-method-names }
现在,像 `createItemItemsPost` 这样的生成方法名看起来不太简洁:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...这是因为客户端生成器会把每个*路径操作*的 OpenAPI 内部**操作 ID(operation ID)**用作方法名的一部分。
OpenAPI 要求每个操作 ID 在所有*路径操作*中都是唯一的,因此 FastAPI 会使用**函数名**、**路径**和**HTTP 方法/操作**来生成操作 ID,以确保其唯一性。
接下来我会告诉你如何改进。🤓
## 自定义操作 ID 与更好的方法名 { #custom-operation-ids-and-better-method-names }
你可以**修改**这些操作 ID 的**生成**方式,使之更简单,从而在客户端中得到**更简洁的方法名**。
在这种情况下,你需要用其他方式确保每个操作 ID 依然是**唯一**的。
例如,你可以确保每个*路径操作*都有一个标签,然后基于**标签**和*路径操作***名称**(函数名)来生成操作 ID。
### 自定义唯一 ID 生成函数 { #custom-generate-unique-id-function }
FastAPI 为每个*路径操作*使用一个**唯一 ID**,它既用于**操作 ID**,也用于请求或响应里任何需要的自定义模型名称。
你可以自定义这个函数。它接收一个 `APIRoute` 并返回一个字符串。
例如,这里使用第一个标签(你很可能只有一个标签)和*路径操作*名称(函数名)。
然后你可以把这个自定义函数通过 `generate_unique_id_function` 参数传给 **FastAPI**:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### 使用自定义操作 ID 生成 TypeScript 客户端 { #generate-a-typescript-client-with-custom-operation-ids }
现在再次生成客户端,你会看到方法名已经改进:
如你所见,方法名现在由标签和函数名组成,不再包含 URL 路径和 HTTP 操作的信息。
### 为客户端生成器预处理 OpenAPI 规范 { #preprocess-the-openapi-specification-for-the-client-generator }
生成的代码中仍有一些**重复信息**。
我们已经知道这个方法与 **items** 有关,因为它位于 `ItemsService`(来自标签),但方法名里仍然带有标签名前缀。😕
通常我们仍然希望在 OpenAPI 中保留它,以确保操作 ID 的**唯一性**。
但对于生成的客户端,我们可以在生成之前**修改** OpenAPI 的操作 ID,只是为了让方法名更美观、更**简洁**。
我们可以把 OpenAPI JSON 下载到 `openapi.json` 文件中,然后用如下脚本**移除这个标签前缀**:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
这样,操作 ID 会从 `items-get_items` 之类的名字重命名为 `get_items`,从而让客户端生成器生成更简洁的方法名。
### 使用预处理后的 OpenAPI 生成 TypeScript 客户端 { #generate-a-typescript-client-with-the-preprocessed-openapi }
因为最终结果现在保存在 `openapi.json` 中,你需要更新输入位置:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
生成新客户端后,你将拥有**简洁的方法名**,并具备**自动补全**、**内联错误**等功能:
## 优点 { #benefits }
使用自动生成的客户端时,你会获得以下内容的**自动补全**:
* 方法
* 请求体中的数据、查询参数等
* 响应数据
你还会为所有内容获得**内联错误**。
每当你更新后端代码并**重新生成**前端时,新的*路径操作*会作为方法可用,旧的方法会被移除,其他任何更改都会反映到生成的代码中。🤓
这也意味着如果有任何变更,它会自动**反映**到客户端代码中。而当你**构建**客户端时,如果所用数据存在任何**不匹配**,它会直接报错。
因此,你可以在开发周期的早期就**发现许多错误**,而不必等到错误在生产环境中暴露给最终用户后再去调试问题所在。✨