openapi-fetch实战:5KB超轻量级TypeScript请求客户端使用指南
你还在为API请求类型安全问题烦恼吗?还在忍受庞大的HTTP客户端库吗?本文将带你掌握openapi-fetch——一个仅5KB大小、零运行时依赖的超轻量级TypeScript请求客户端,让你轻松实现类型安全的API调用。读完本文,你将能够:快速搭建openapi-fetch环境、掌握基础与高级用法、在React等主流框架中集成使用,并了解性能优化技巧。
为什么选择openapi-fetch?
openapi-fetch是一个基于OpenAPI schema生成的类型安全fetch客户端,它具有以下核心优势:
- 极致轻量化:仅5KB大小,相比axios(32KB)、superagent(55KB)等传统库,极大减少了客户端资源占用。
- 类型安全:自动从OpenAPI schema生成TypeScript类型,确保请求参数和响应数据的类型正确性。
- 高性能:基准测试显示,openapi-fetch的GET请求性能远超同类库,达到300k ops/s,是openapi-typescript-fetch的2倍,axios的1.3倍。
- 零运行时依赖:几乎没有运行时开销,不会给你的应用带来额外负担。
- 框架无关:可与React、Vue、Svelte等主流前端框架无缝集成,也可用于原生JavaScript项目。
性能对比
以下是openapi-fetch与其他主流HTTP客户端库的性能对比:
| Library | Size (min) | “GET” request* |
|---|---|---|
| openapi-fetch | 5 kB | 300k ops/s (fastest) |
| openapi-typescript-fetch | 4 kB | 150k ops/s (2× slower) |
| axios | 32 kB | 225k ops/s (1.3× slower) |
| superagent | 55 kB | 50k ops/s (6× slower) |
| openapi-typescript-codegen | 367 kB | 100k ops/s (3× slower) |
* 基准测试结果仅供参考,实际性能可能因机器和浏览器环境而异。相对性能对比更为可靠。
环境搭建
安装依赖
首先,安装openapi-fetch及其依赖:
npm i openapi-fetch
npm i -D openapi-typescript typescript
生成TypeScript类型
使用openapi-typescript从你的OpenAPI schema生成TypeScript类型文件:
npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts
例如,如果你有一个名为api.yaml的OpenAPI schema文件,可以通过以下命令生成类型:
npx openapi-typescript ./api.yaml -o ./src/api-types.d.ts
配置TypeScript
强烈建议在tsconfig.json中启用noUncheckedIndexedAccess选项,以获得更严格的类型检查:
{
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}
同时,添加类型检查脚本到package.json:
{
"scripts": {
"test:ts": "tsc --noEmit"
}
}
运行npm run test:ts即可进行类型检查,建议在CI流程中添加此步骤以捕获类型错误。
基础使用
创建客户端实例
首先,导入createClient函数和生成的TypeScript类型,创建API客户端实例:
import createClient from "openapi-fetch";
import type { paths } from "./src/lib/api/v1.d.ts"; // 导入生成的类型
const client = createClient<paths>({
baseUrl: "https://api.example.com/v1/"
});
发送GET请求
使用客户端实例发送GET请求,openapi-fetch会自动推断请求参数和响应数据的类型:
const { data, error } = await client.GET("/blogposts/{post_id}", {
params: {
path: { post_id: "123" }, // 路径参数
query: { version: 2 } // 查询参数
}
});
if (data) {
console.log("获取文章成功:", data);
} else if (error) {
console.error("获取文章失败:", error);
}
发送POST请求
发送POST请求同样简单,请求体也会进行类型检查:
const { data, error } = await client.POST("/blogposts", {
body: {
title: "openapi-fetch实战",
content: "这是一篇关于openapi-fetch的实战文章",
author: "John Doe"
}
});
openapi-fetch的一大优势是无需手动编写类型,所有请求参数和响应数据的类型都由OpenAPI schema自动生成,确保了类型安全。
高级特性
请求参数序列化
openapi-fetch支持自定义查询参数和请求体的序列化方式,以适应不同后端API的需求。
查询参数序列化
可以通过querySerializer选项自定义查询参数的序列化方式,支持数组和对象的不同序列化风格:
const client = createClient({
baseUrl: "https://api.example.com/v1/",
querySerializer: {
array: {
style: "pipeDelimited", // 支持"form"、"spaceDelimited"、"pipeDelimited"
explode: true
},
object: {
style: "form", // 支持"form"、"deepObject"
explode: true
}
}
});
不同序列化风格的效果如下:
| 风格 | 数组id = [3, 4, 5] | 对象id = {"role": "admin", "firstName": "Alex"} |
|---|---|---|
| form (默认) | /users?id=3&id=4&id=5 | /users?role=admin&firstName=Alex |
| pipeDelimited | /users?id=3|4|5 | - |
| deepObject (对象默认) | - | /users?id[role]=admin&id[firstName]=Alex |
路径参数序列化
openapi-fetch支持OpenAPI 3.1规范中定义的路径参数序列化风格,如simple、label、matrix等:
| 模板 | 风格 | 示例 |
|---|---|---|
/users/{id} | simple (默认) | /users/5 |
/users/{.id} | label | /users/.5 |
/users/{;id} | matrix | /users/;id=5 |
中间件
openapi-fetch支持中间件功能,可用于请求拦截、响应处理、错误统一处理等场景。
错误处理中间件
以下是一个统一错误处理的中间件示例:
import createClient, { type Middleware } from "openapi-fetch";
import type { paths } from "./src/lib/api/v1.d.ts";
const errorHandler: Middleware = {
async onResponse({ response }) {
if (response.status >= 400) {
const contentType = response.headers.get("content-type");
const errorBody = contentType?.includes("json")
? await response.clone().json()
: await response.clone().text();
throw new Error(`API Error: ${JSON.stringify(errorBody)}`);
}
return response;
}
};
const client = createClient<paths>({ baseUrl: "https://api.example.com/v1/" });
client.use(errorHandler); // 注册中间件
认证中间件
可以使用中间件添加认证令牌:
const authMiddleware: Middleware = {
async onRequest({ request }) {
const token = localStorage.getItem("auth_token");
if (token) {
request.headers.set("Authorization", `Bearer ${token}`);
}
return request;
}
};
client.use(authMiddleware);
自定义Fetch实现
openapi-fetch默认使用全局的fetch函数,你也可以指定自定义的fetch实现,例如在Node.js环境中使用node-fetch:
import fetch from "node-fetch";
const client = createClient({
baseUrl: "https://api.example.com/v1/",
fetch: fetch as unknown as typeof globalThis.fetch
});
框架集成
React + React Query
openapi-fetch与React Query配合使用可以实现数据获取、缓存和状态管理的完美结合。首先创建API客户端:
// src/lib/api/index.ts
import createClient, { type Middleware } from "openapi-fetch";
import type { paths } from "./v1";
const throwOnError: Middleware = {
async onResponse({ response }) {
if (response.status >= 400) {
const body = response.headers.get("content-type")?.includes("json")
? await response.clone().json()
: await response.clone().text();
throw new Error(JSON.stringify(body));
}
return undefined;
},
};
const client = createClient<paths>({ baseUrl: "https://catfact.ninja/" });
client.use(throwOnError);
export default client;
然后,创建自定义hooks使用React Query:
// src/hooks/queries.ts
import { useQuery } from "@tanstack/react-query";
import client from "../lib/api";
export function useCatFact() {
return useQuery({
queryKey: ["cat-fact"],
queryFn: async () => {
const { data } = await client.GET("/fact");
return data;
}
});
}
在组件中使用:
import { useCatFact } from "../hooks/queries";
function CatFactComponent() {
const { data, isLoading, error } = useCatFact();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {(error as Error).message}</div>;
return <div>Cat Fact: {data?.fact}</div>;
}
Next.js集成
在Next.js中,可以利用其内置的fetch缓存功能,结合openapi-fetch实现服务端和客户端的数据获取:
// app/page.tsx
import createClient from "openapi-fetch";
import type { paths } from "@/lib/api/v1.d.ts";
const client = createClient<paths>({ baseUrl: "https://api.example.com/v1/" });
export default async function Home() {
// 服务端获取数据
const { data } = await client.GET("/featured-posts", {
next: { revalidate: 60 } // 缓存60秒
});
return (
<main>
<h1>Featured Posts</h1>
<ul>
{data?.posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
);
}
Vue 3集成
在Vue 3中,可以使用组合式API结合openapi-fetch:
<script setup lang="ts">
import { ref } from 'vue';
import createClient from 'openapi-fetch';
import type { paths } from '@/generated/api';
const client = createClient<paths>({ baseUrl: 'https://api.example.com/v1/' });
const posts = ref(null);
const error = ref(null);
async function fetchPosts() {
try {
const { data } = await client.GET('/posts');
posts.value = data;
} catch (err) {
error.value = err;
}
}
fetchPosts();
</script>
<template>
<div v-if="posts">
<h1>Posts</h1>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else>Loading...</div>
</template>
SvelteKit集成
SvelteKit中可以在load函数中使用openapi-fetch获取数据:
<!-- src/routes/+page.svelte -->
<script lang="ts">
export let data;
</script>
<div>
<h1>User Profile</h1>
<p>Name: {data.user.name}</p>
<p>Email: {data.user.email}</p>
</div>
// src/routes/+page.ts
import createClient from 'openapi-fetch';
import type { paths } from '$lib/api/v1.d.ts';
const client = createClient<paths>({ baseUrl: 'https://api.example.com/v1/' });
export async function load() {
const { data } = await client.GET('/user/profile');
return { user: data };
}
最佳实践
错误处理策略
建议使用中间件统一处理错误,避免在每个请求中重复编写错误处理逻辑:
// error-middleware.ts
import type { Middleware } from 'openapi-fetch';
export const errorMiddleware: Middleware = {
async onResponse({ response }) {
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(
errorData?.message || `API Error: ${response.status} ${response.statusText}`
);
}
return response;
}
};
然后在创建客户端时注册:
import { errorMiddleware } from './error-middleware';
const client = createClient<paths>({ baseUrl: "https://api.example.com/v1/" });
client.use(errorMiddleware);
类型检查自动化
将类型检查集成到开发流程中,确保API类型的正确性:
- 添加预提交钩子,在提交代码前自动运行类型检查
- 在CI/CD流程中添加类型检查步骤
- 使用
tsc --watch在开发过程中实时监控类型变化
API版本管理
对于多版本API,可以创建不同的客户端实例:
// api-v1.ts
import createClient from 'openapi-fetch';
import type { paths as pathsV1 } from './v1.d.ts';
export const clientV1 = createClient<pathsV1>({ baseUrl: "https://api.example.com/v1/" });
// api-v2.ts
import createClient from 'openapi-fetch';
import type { paths as pathsV2 } from './v2.d.ts';
export const clientV2 = createClient<pathsV2>({ baseUrl: "https://api.example.com/v2/" });
总结
openapi-fetch作为一个仅5KB的超轻量级TypeScript请求客户端,以其极致的性能和类型安全特性,为现代前端开发提供了高效的API调用解决方案。通过本文的介绍,你已经掌握了openapi-fetch的环境搭建、基础使用、高级特性和框架集成方法。
无论是在React、Vue、Svelte等前端框架中,还是在Node.js后端环境中,openapi-fetch都能帮助你轻松实现类型安全的API调用,减少错误,提高开发效率。立即尝试openapi-fetch,体验类型安全的API请求新方式!
官方文档:docs/openapi-fetch/index.md API参考:docs/openapi-fetch/api.md 示例代码库:packages/openapi-fetch/examples/
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




