openapi-fetch实战:5KB超轻量级TypeScript请求客户端使用指南

openapi-fetch实战:5KB超轻量级TypeScript请求客户端使用指南

【免费下载链接】openapi-typescript Generate TypeScript types from OpenAPI 3 specs 【免费下载链接】openapi-typescript 项目地址: https://gitcode.com/gh_mirrors/ope/openapi-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 logo

性能对比

以下是openapi-fetch与其他主流HTTP客户端库的性能对比:

LibrarySize (min)“GET” request*
openapi-fetch5 kB300k ops/s (fastest)
openapi-typescript-fetch4 kB150k ops/s (2× slower)
axios32 kB225k ops/s (1.3× slower)
superagent55 kB50k ops/s (6× slower)
openapi-typescript-codegen367 kB100k 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 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>;
}

查看完整React Query示例

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>
  );
}

查看Next.js完整示例

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>

查看Vue 3完整示例

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 };
}

查看SvelteKit完整示例

最佳实践

错误处理策略

建议使用中间件统一处理错误,避免在每个请求中重复编写错误处理逻辑:

// 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类型的正确性:

  1. 添加预提交钩子,在提交代码前自动运行类型检查
  2. 在CI/CD流程中添加类型检查步骤
  3. 使用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/

【免费下载链接】openapi-typescript Generate TypeScript types from OpenAPI 3 specs 【免费下载链接】openapi-typescript 项目地址: https://gitcode.com/gh_mirrors/ope/openapi-typescript

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值