quick-fy 是一个轻量级、可扩展的基于 fetch 的 HTTP 请求库,适用于浏览器和 Node.js 环境。
- 基于原生 fetch API
- 提供全局钩子函数(onSuccess, onError, onComplete)
- 中间件(Middleware)洋葱模型,支持网络层拦截和修改
- 内置常用中间件:logger、retry、timeout、body、params、headers
- 支持自定义元数据传递
- 类型安全,使用 TypeScript 编写
npm install quick-fyimport { createFy, fy } from 'quick-fy'
import { body } from 'quick-fy/middlewares/body'
import { params } from 'quick-fy/middlewares/params'
// 使用默认 fy 实例
const data = await fy.get('https://api.example.com/users')
// 创建自定义实例
const api = createFy({
baseURL: 'https://api.example.com',
middlewares: [params(), body()],
})
const users = await api.get<User[]>('/users')
const user = await api.post<User>('/users', {
data: { name: 'Alice' },
params: { source: 'web' },
headers: { 'X-Request-From': 'quick-start' },
})quick-fy 使用单个 options 参数。核心 options 支持 fetch 原生字段(如 body、headers、signal、credentials)以及 responseType、meta。
data 和 params 由对应 middleware 扩展类型并处理;需要请求体和查询参数能力时,请导入并注册 body() 和 params()。
记录请求和响应信息,包含耗时统计。默认使用 console.groupCollapsed 输出折叠分组,标题展示请求方法、path、状态和耗时,并用绿色/红色区分成功与失败;展开后可以查看完整 URL、params、data、meta 和 config。
import { createFy } from 'quick-fy'
import { logger } from 'quick-fy/middlewares/logger'
const fy = createFy()
fy.use(logger())
// 输出示例:
// ← GET /users 200 (123ms)
// url https://api.example.com/users
// params { page: 1 }
// data { name: 'Alice' }自定义回调:
fy.use(logger({
onRequest: (method, url) => console.log(`Sending ${method} ${url}`),
onResponse: (method, url, status, duration) => console.log(`${status} in ${duration}ms`),
onError: (method, url, error, duration) => console.error(`Failed: ${String(error)}`),
}))| 选项 | 类型 | 默认行为 |
|---|---|---|
onRequest |
(method, url) => void |
自定义请求开始日志 |
onResponse |
(method, url, status, duration) => void |
自定义请求成功日志 |
onError |
(method, url, error, duration) => void |
自定义请求失败日志 |
请求失败时自动重试,默认仅重试网络错误(TypeError)。
import { retry } from 'quick-fy/middlewares/retry'
fy.use(retry())
// 自定义配置
fy.use(retry({
retries: 5,
delay: 2000,
retryOnStatus: [502, 503, 504],
shouldRetry: (_error, attempt) => attempt < 3,
}))| 选项 | 类型 | 默认值 |
|---|---|---|
retries |
number |
3 |
delay |
number (ms) |
1000 |
shouldRetry |
(error, attempt) => boolean |
仅 TypeError 时重试 |
retryOnStatus |
number[] | (response, attempt) => boolean |
[] |
使用 AbortController 实现请求超时控制。
import { timeout, TimeoutError } from 'quick-fy/middlewares/timeout'
fy.use(timeout({ timeout: 5000 }))
try {
await fy.get('/slow-api')
}
catch (error) {
if (error instanceof TimeoutError) {
console.log('请求超时')
}
}| 选项 | 类型 | 说明 |
|---|---|---|
timeout |
number (ms) |
必填,超时时间 |
导入后会为 FyRequestOptions 扩展 data 字段,并在注册 body() 后将 options.data 转换为请求体。普通对象和数组会序列化为 JSON,FormData、Blob、URLSearchParams、字符串会直接作为 body 传入。若同时传入原生 body 和 data,显式 body 优先。
import { body } from 'quick-fy/middlewares/body'
fy.use(body())
await fy.post('/users', {
data: { name: 'Alice', age: 25 },
})
// 自动设置 Content-Type: application/json 并序列化 body导入后会为 FyRequestOptions 扩展 params 字段,并在注册 params() 后将 options.params 序列化为 URL 查询字符串。数组会展开为重复 key,null 和 undefined 会被跳过。
import { params } from 'quick-fy/middlewares/params'
fy.use(params())
await fy.get('/users', {
params: {
page: 1,
size: 20,
tag: ['new', 'hot'],
},
})
// 实际请求: /users?page=1&size=20&tag=new&tag=hot如果项目需要兼容后端约定的特殊格式,可以传入自定义 serializer。例如使用 qs 输出 tag[]=new&tag[]=hot:
import qs from 'qs'
import { params } from 'quick-fy/middlewares/params'
fy.use(params({
serializer: value => qs.stringify(value, {
arrayFormat: 'brackets',
skipNulls: true,
}),
}))
await fy.get('/users', {
params: {
tag: ['new', 'hot'],
empty: undefined,
},
})quick-fy 只接收序列化函数,不内置依赖 qs。需要这种格式时由使用方自行安装:
pnpm add qs
pnpm add -D @types/qs为所有请求添加默认 headers,不会覆盖已存在的同名 header。可以直接传入 HeadersInit,也可以传入函数;函数会在每次请求前执行,适合读取运行时才有的 token。
import { headers } from 'quick-fy/middlewares/headers'
const fy = createFy({
middlewares: [
headers({
'Authorization': 'Bearer token123',
'X-App-Version': '1.0.0',
}),
],
})动态 headers:
const fy = createFy({
middlewares: [
headers(method => ({
'Authorization': `Bearer ${getToken()}`,
'X-Trace-Id': method.meta?.traceId,
})),
],
})
await fy.get('/users', {
meta: {
traceId: 'req-001',
},
})Middleware 作用于 fetch 调用的网络层,采用洋葱模型(onion model)执行。每个 middleware 接收 next 并返回新的处理函数,可以在请求前后添加逻辑。
import type { Middleware } from 'quick-fy'
// 自定义中间件
const rewriteUrl: Middleware = next => async (url, config, meta) => {
return next(url.replace('api/v1', 'api/v2'), config, meta)
}
const fy = createFy()
fy.use(rewriteUrl)中间件按注册顺序执行,形成洋葱模型:
A-before → B-before → fetch → B-after → A-after
对于常见组合,推荐按“请求准备 → 观察 → 重试 → 单次超时”的顺序注册:
import { body } from 'quick-fy/middlewares/body'
import { headers } from 'quick-fy/middlewares/headers'
import { logger } from 'quick-fy/middlewares/logger'
import { params } from 'quick-fy/middlewares/params'
import { retry } from 'quick-fy/middlewares/retry'
import { timeout } from 'quick-fy/middlewares/timeout'
const fy = createFy({
middlewares: [
params(),
body(),
headers(() => ({ Authorization: `Bearer ${getToken()}` })),
logger(),
retry(),
timeout({ timeout: 5000 }),
],
})这表示 logger() 只记录一次逻辑请求,并能看到处理后的 URL 和请求配置;retry() 会包住 timeout(),因此每次 retry attempt 都有独立超时。如果希望每次重试都重新获取 token,可手动把 headers() 放到 retry() 后面。
顺序本身就是语义,quick-fy 不会自动重排 middleware。常见反向顺序的含义:
[timeout(), retry()]:整个重试流程共享一个总超时。[retry(), logger()]:每次 retry attempt 都会记录日志。[retry(), headers()]:每次 retry attempt 都会重新调用动态 headers 函数。
2.0 不提供清空中间件的方法。需要干净实例时,请重新调用 createFy() 创建。
自定义 middleware 可以统一实现请求前、响应后、错误处理和短路请求等扩展能力。
import type { Middleware } from 'quick-fy'
const requestLogger: Middleware = next => async (url, config, meta) => {
console.log('请求:', url)
const response = await next(url, config, meta)
console.log('响应状态:', response.status)
return response
}
const fy = createFy({
middlewares: [requestLogger],
})创建一个新的 fy 实例。
| 字段 | 类型 | 说明 |
|---|---|---|
baseURL |
string |
基础 URL,非 http 开头的请求会自动拼接 |
responded |
RespondedHook |
全局响应钩子(onSuccess, onError, onComplete) |
middlewares |
Middleware[] |
中间件数组 |
| 方法 | 说明 |
|---|---|
request<T>(url, options?) |
发送请求 |
get<T>(url, options?) |
GET 请求 |
post<T>(url, options?) |
POST 请求 |
put<T>(url, options?) |
PUT 请求 |
delete<T>(url, options?) |
DELETE 请求 |
patch<T>(url, options?) |
PATCH 请求 |
use(middleware) |
添加中间件 |
推荐通过第二个参数传递请求配置。核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
responseType |
'json' | 'text' | 'blob' | 'arraybuffer' |
指定响应数据类型 |
meta |
Record<string, any> |
仅传递给 middleware,不会进入 fetch |
body / headers / signal / credentials 等 |
RequestInit 字段 |
原样传给 fetch |
middleware 可以通过模块扩展增加 options 字段:
| 字段 | 来源 | 说明 |
|---|---|---|
data |
quick-fy/middlewares/body |
请求体数据,需导入并注册 body() |
params |
quick-fy/middlewares/params |
查询参数,需导入并注册 params() |
await fy.post('/users', {
data: { name: 'Alice' },
params: { source: 'admin' },
responseType: 'json',
meta: { traceId: 'req-001', skipAuth: false },
signal: controller.signal,
})第三方 middleware 应通过 options.meta 扩展自定义字段,不要把扩展字段放在 options 顶层。顶层字段会被归一化为 RequestInit 或 quick-fy 内置字段,meta 才是传递给 middleware 的扩展上下文。
import type { Middleware } from 'quick-fy'
declare module 'quick-fy' {
interface Meta {
skipAuth?: boolean
traceId?: string
}
}
export function auth(): Middleware {
return next => async (url, config, meta) => {
if (meta.skipAuth) {
return next(url, config, meta)
}
const headers = new Headers(config.headers)
headers.set('Authorization', 'Bearer token')
return next(url, { ...config, headers }, meta)
}
}
await fy.get('/users', {
meta: {
skipAuth: true,
traceId: 'req-001',
},
})核心字段如 responseType 放在 options 顶层;data、params 由内置 middleware 扩展后放在顶层;第三方控制字段如 skipAuth、traceId、retryGroup 放在 options.meta。
请求失败时抛出,包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
message |
string |
错误信息 |
status |
number |
HTTP 状态码 |
response |
Response |
原始 Response 对象 |
method |
Method |
请求方法信息 |