- 开发工具
- 代码生成
- 后端
【免费下载链接】openapi-typescript
Generate TypeScript types from OpenAPI 3 specs
本指南基于 openapi-fetch 仓库中的 SvelteKit 示例应用,完整讲解如何在 Svelte / SvelteKit 项目中集成 openapi-fetch,实现从 OpenAPI 规范生成 TypeScript 类型、构建类型安全 API 客户端,并在客户端组件与 SvelteKit Page Data(服务端加载)两种场景下发请求的完整流程。读完本文,你将掌握
createClient客户端初始化、client.GET()类型安全的请求/响应推断、SvelteKitload函数中自定义fetch的注入,以及服务端渲染下响应头序列化的必要配置。
示例项目总览
示例应用位于 packages/openapi-fetch/examples/sveltekit,是一个基于 SvelteKit 2.x + Svelte 5 的完整可运行项目,演示了 openapi-fetch 在 SvelteKit 中的两种典型接入模式:
- 纯客户端模式(Clientside):在
.svelte组件的onMount生命周期中直接调用 API,见 src/routes/+page.svelte; - Page Data(服务端加载)模式:通过 SvelteKit 的
+page.ts的load函数在服务端获取数据并传给组件,见 src/routes/page-data/+page.svelte 与其配套的 src/routes/page-data/+page.ts。
示例使用的远端 API 是 Cat Facts API(https://catfact.ninja/),其 OpenAPI 3.0 规范文件已保存在本地:src/lib/api/v1.json,并预先生成了对应的 TypeScript 类型声明文件 src/lib/api/v1.d.ts。
快速启动
安装依赖
示例项目根目录下执行:
pnpm i项目的依赖清单见 package.json,其中与本文主题直接相关的依赖是:
| 依赖 | 说明 |
|---|---|
openapi-fetch(workspace:^) | 类型安全的 fetch 客户端运行时库,体积小、零依赖 |
openapi-typescript(workspace:^) | 负责把 OpenAPI 规范生成 TypeScript 类型 |
@sveltejs/kit、@sveltejs/vite-plugin-svelte、svelte | SvelteKit 框架本身(示例使用 Svelte 5 的 runes 语法) |
vite | 构建与开发服务器 |
typescript、svelte-check | 类型检查工具(pnpm run check) |
注意:这里openapi-fetch与openapi-typescript均以workspace:^方式引用当前 monorepo 内的源码包。在实际业务项目中,请改为安装 npm 上发布的正式版本,例如:
pnpm add openapi-fetch pnpm add -D openapi-typescript启动开发服务器
pnpm run devdev脚本在 package.json 中定义为vite dev。启动后访问http://localhost:5173即可看到运行中的示例页面,顶部有两个入口:Client(客户端模式)与Page Data(服务端加载模式)。
项目还内置了类型检查脚本:
pnpm run check # svelte-kit sync && svelte-check --tsconfig ./tsconfig.json pnpm run check:watch # 监听模式项目结构与关键文件
src/ ├── lib/ │ └── api/ │ ├── index.ts # 创建并导出 openapi-fetch 客户端(唯一的客户端实例) │ ├── v1.d.ts # openapi-typescript 生成的类型(paths 接口等) │ └── v1.json # Cat Facts API 的 OpenAPI 3.0 规范 ├── routes/ │ ├── +page.svelte # 客户端模式示例 │ └── page-data/ │ ├── +page.svelte # 消费服务端 load 数据的组件 │ └── +page.ts # 服务端 load 函数 ├── app.d.ts ├── app.html └── hooks.server.ts # 服务端钩子,配置响应头序列化其余配置文件包括 svelte.config.js(使用vitePreprocess与adapter-auto)、vite.config.ts(仅引入sveltekit()插件)、tsconfig.json(strict模式、moduleResolution: "bundler")与 app.html(SvelteKit 入口 HTML)。
第一步:用 openapi-typescript 生成类型
SvelteKit 本身并不感知 API 的类型,类型安全的核心前提是把 OpenAPI 规范文件转换为 TypeScript 类型声明。示例中这一步骤已完成,产物是 src/lib/api/v1.d.ts,文件头部注释明确标注:
/** * This file was auto-generated by openapi-typescript. * Do not make direct changes to the file. */该文件导出了一个核心接口paths,按 URL 路径组织所有端点。例如v1.json规范中定义的/fact路径被映射为:
"/fact": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** Get Random Fact @description Returns a random fact */ get: operations["getRandomFact"]; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; };每个 HTTP 动词对应规范中声明的 operation;未声明的方法被标记为never,从而在调用client.GET("/fact")时强制走类型检查。从源码结构看,这就是 openapi-typescript 的核心能力:解析 OpenAPI 3 规范(JSON 或 YAML),输出包含paths、components、operations等类型的.d.ts文件。
在真实项目中,可在 package.json 中注册生成脚本(示例如下,需根据实际 schema 路径调整):
{ "scripts": { "generate:api": "openapi-typescript ./src/lib/api/v1.json -o ./src/lib/api/v1.d.ts" } }生成的类型文件建议纳入版本控制;当上游 OpenAPI 规范变更时,重新运行生成命令即可让客户端类型与接口定义保持同步。
第二步:创建类型安全的客户端实例
客户端实例集中在 src/lib/api/index.ts 创建并默认导出,整个应用共享同一个实例:
import createClient from "openapi-fetch"; import type { paths } from "./v1"; const client = createClient<paths>({ baseUrl: "https://catfact.ninja/" }); export default client;要点说明:
createClient<paths>是泛型工厂函数,paths类型来自上一步生成的v1.d.ts,此后所有请求的路径、参数、响应体都会被该类型约束;baseUrl指向远端 API 的根地址;SvelteKit 的路径别名$lib使$lib/api/index.js可以引用src/lib目录下的文件(别名在 svelte.config.js 与 tsconfig.json 中按 SvelteKit 约定处理);- openapi-fetch 的客户端是零运行时依赖的轻量封装,内部仍然基于标准
fetch,因此可以无缝运行在浏览器与 Node.js / SvelteKit 服务端两种环境。
第三步:两种请求模式实战
模式一:纯客户端请求(Clientside)
在 src/routes/+page.svelte 中,请求发生在浏览器端的组件生命周期内:
<script lang="ts"> import { onMount } from "svelte"; import client from "$lib/api/index.js"; let fact: Awaited<ReturnType<typeof getFact>> | undefined = $state(undefined); async function getFact() { return client.GET("/fact", { params: { query: { max_length: 500 }, }, }); } onMount(async () => { fact = await getFact(); }); </script> <div> <p>Example: Client | <a href="/page-data">Page Data</a></p> {#if fact} {#if fact.error} <div>There was an error: {fact.error}</div> {:else} <pre><code>{JSON.stringify(fact.data, undefined, 2)}</code></pre> {/if} {/if} <button type="button" onclick={async () => (fact = await getFact())}> Another fact! </button> </div>值得注意的实现细节:
client.GET("/fact", { params: { query: { max_length: 500 } } })中,路径/fact、查询参数max_length均由paths类型严格校验;一旦拼错路径或参数,TypeScript 会立即报错;- 返回值是
{ data, error, response }结构(data 在 2xx 时携带响应体,error 在非 2xx 时携带错误信息),所以模板中用{#if fact.error} ... {:else} ... {/if}做分支渲染; - 示例使用了 Svelte 5 的 runes 语法:
$state声明响应式状态,onclick内联事件直接调用getFact()拉取新的随机事实并刷新页面; - 该模式在组件挂载(
onMount)后才发起请求,因此适合数据无需 SEO、可接受首屏后异步加载的场景。
模式二:Page Data 服务端加载(推荐)
SvelteKit 更推荐在load函数中预取数据,再通过dataprop 注入组件。这一步在 src/routes/page-data/+page.ts 完成:
import type { PageLoad } from "./$types"; import client from "$lib/api/index.js"; // Note: this uses Svelte’s custom fetcher as an example, but Node’s // native fetch works, too. See Svelte’s docs to learn the difference: // @see https://svelte.dev/docs/kit/load#Making-fetch-requests export const load: PageLoad = async ({ fetch }) => { const fact = await client.GET("/fact", { params: { query: { max_length: 500 } }, fetch, }); return { fact: { data: fact.data, error: fact.error, }, }; };两个关键点:
- 注入 SvelteKit 的
fetch:openapi-fetch 的请求选项支持传入自定义fetch函数。这里把 SvelteKitload上下文中的fetch传给client.GET(),使得请求复用 SvelteKit 的 fetch 基础设施——它会在服务端渲染时直接请求目标 API,并把结果连同响应序列化到客户端,还能自动处理相对 URL、凭证与缓存策略。从源码结构看,openapi-fetch 在 packages/openapi-fetch/src/index.js 中优先使用传入的fetch,否则回退到全局fetch,因此不传也完全可行(使用 Node 原生 fetch); - 类型安全的返回结构:
load返回{ fact: { data, error } },+page.svelte通过PageProps类型自动获得精确推断。
组件侧 src/routes/page-data/+page.svelte 消费该数据:
<script lang="ts"> import type { PageProps } from "./$types"; let { data }: PageProps = $props(); </script> <div> <p>Example: <a href="/">Client</a> | Page Data</p> {#if data.fact.error} <div>There was an error: {data.fact.error.message}</div> {:else if data.fact.data} <pre><code>{JSON.stringify(data.fact.data, undefined, 2)}</code></pre> {:else} <div>Loading...</div> {/if} <button type="button" onclick={() => location.reload()}>Another fact!</button> </div>组件使用 Svelte 5 的$props()rune 解构出data,其类型由./$types中的PageProps提供(该文件由svelte-kit sync生成,与+page.ts的返回类型保持同步)。这里通过data.fact.error.message展示错误详情,并提供了三种分支:出错、成功、加载中。由于加载发生在服务端,页面首屏即可拿到数据,对 SEO 与首屏性能更友好。
第四步:服务端响应头序列化配置
这是 SvelteKit + openapi-fetch 集成中容易被忽略却至关重要的一步。SvelteKit 出于安全考虑,默认不会把服务端 fetch 的响应头序列化给客户端,但 openapi-fetch 需要依赖content-length响应头来判断空响应(例如 204 No Content)。若缺失该头,空响应体的解析可能出错。
src/hooks.server.ts 中的服务端钩子解决了这个问题:
import type { Handle } from "@sveltejs/kit"; export const handle: Handle = async ({ event, resolve }) => { return resolve(event, { filterSerializedResponseHeaders(name) { // SvelteKit doesn't serialize any headers on server-side fetches by default but openapi-fetch uses this header for empty responses. return name === "content-length"; }, }); };filterSerializedResponseHeaders返回true的响应头才会被序列化。这里仅放行content-length,既补全了 openapi-fetch 判断空响应所需的信息,又保持了最小化的头暴露面。代码注释直接点明了原因:SvelteKit 默认不序列化任何服务端 fetch 的响应头,而 openapi-fetch 使用该头处理空响应。
类型安全验证与开发体验
示例配套的类型检查命令pnpm run check(即svelte-kit sync && svelte-check)会在开发期验证整个链路的类型一致性:
svelte-kit sync生成.svelte-kit/下的$types等声明文件,保证./$types导入可用;svelte-check依据 tsconfig.json(strict: true、moduleResolution: "bundler"、allowJs/checkJs)对 Svelte 组件与 TS 代码做全量类型检查。
你可以实际验证类型安全的效果:把client.GET("/fact")改成client.GET("/facts")(合法路径)并传入错误的查询参数名,或改成不存在的路径/foo,保存后运行pnpm run check,TypeScript 会立刻指出类型不匹配——这正是从 OpenAPI 规范 → 生成类型 → 客户端调用这一整条类型链路的收益。
小结与延伸
通过这个 SvelteKit 示例,可以看到 openapi-fetch 的完整集成套路:
- 准备:持有 OpenAPI 3 规范(如 v1.json),用 openapi-typescript 生成类型(如 v1.d.ts);
- 初始化:在 src/lib/api/index.ts 中
createClient<paths>({ baseUrl })创建单例客户端; - 消费:客户端组件用
onMount+client.GET(),服务端用load函数 + 注入 SvelteKitfetch; - 加固:在 hooks.server.ts 放行
content-length响应头,保证空响应处理正确。
两种模式可以按需组合:对 SEO 敏感或需要首屏数据的页面走 Page Data 服务端加载,对交互频繁、实时性要求高的局部区域走客户端请求。项目根目录的 docs/openapi-fetch 文档还提供了 middleware、测试(docs/openapi-fetch/testing.md)等进阶用法,可作为继续深入 openapi-fetch 的起点。本示例的完整源码均可在 packages/openapi-fetch/examples/sveltekit 目录下查看与运行。
- 开发工具
- 代码生成
- 后端
【免费下载链接】openapi-typescript
Generate TypeScript types from OpenAPI 3 specs
相关推荐
openapi-fetch:基于OpenAPI的类型安全HTTP客户端
openapi fetch:基于OpenAPI的类型安全HTTP客户端 openapi fetch是一个轻量级HTTP客户端库,专为现代Web开发设计,完美结合
开发工具代码生成后端openapi-fetch 完整指南:为 OpenAPI 3 规范构建 6 kB 的类型安全 Fetch 客户端
openapi fetch 完整指南:为 OpenAPI 3 规范构建 6 kB 的类型安全 Fetch 客户端 openapi fetch 是 openapi
开发工具代码生成后端告别命令行!TortoiseGit让Git操作可视化,零基础也能轻松上手
告别命令行!TortoiseGit让Git操作可视化,零基础也能轻松上手 对于很多刚接触版本控制的开发者来说,Git命令行常常让人望而生畏。繁杂的指令、晦涩的参
桌面应用版本控制开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考