Skip to content

Xdy1579883916/quick-fy

Repository files navigation

quick-fy

npm version npm downloads bundle License

quick-fy 是一个轻量级、可扩展的基于 fetch 的 HTTP 请求库,适用于浏览器和 Node.js 环境。

特性

  • 基于原生 fetch API
  • 提供全局钩子函数(onSuccess, onError, onComplete)
  • 中间件(Middleware)洋葱模型,支持网络层拦截和修改
  • 内置常用中间件:logger、retry、timeout、body、params、headers
  • 支持自定义元数据传递
  • 类型安全,使用 TypeScript 编写

安装

npm install quick-fy

快速开始

import { 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 原生字段(如 bodyheaderssignalcredentials)以及 responseTypemetadataparams 由对应 middleware 扩展类型并处理;需要请求体和查询参数能力时,请导入并注册 body()params()

内置中间件

logger - 请求日志

记录请求和响应信息,包含耗时统计。默认使用 console.groupCollapsed 输出折叠分组,标题展示请求方法、path、状态和耗时,并用绿色/红色区分成功与失败;展开后可以查看完整 URL、paramsdatametaconfig

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 自定义请求失败日志

retry - 自动重试

请求失败时自动重试,默认仅重试网络错误(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 []

timeout - 请求超时

使用 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) 必填,超时时间

body - JSON 请求体

导入后会为 FyRequestOptions 扩展 data 字段,并在注册 body() 后将 options.data 转换为请求体。普通对象和数组会序列化为 JSON,FormDataBlobURLSearchParams、字符串会直接作为 body 传入。若同时传入原生 bodydata,显式 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

params - 查询参数

导入后会为 FyRequestOptions 扩展 params 字段,并在注册 params() 后将 options.params 序列化为 URL 查询字符串。数组会展开为重复 key,nullundefined 会被跳过。

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 - 默认请求头

为所有请求添加默认 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

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

自定义 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],
})

API 参考

createFy(options)

创建一个新的 fy 实例。

字段 类型 说明
baseURL string 基础 URL,非 http 开头的请求会自动拼接
responded RespondedHook 全局响应钩子(onSuccess, onError, onComplete)
middlewares Middleware[] 中间件数组

FyInstance 方法

方法 说明
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) 添加中间件

Request Options

推荐通过第二个参数传递请求配置。核心字段如下:

字段 类型 说明
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 顶层;dataparams 由内置 middleware 扩展后放在顶层;第三方控制字段如 skipAuthtraceIdretryGroup 放在 options.meta

FyError

请求失败时抛出,包含以下字段:

字段 类型 说明
message string 错误信息
status number HTTP 状态码
response Response 原始 Response 对象
method Method 请求方法信息

迁移指南

许可证

MIT License © 2024-PRESENT XiaDeYu

About

quick-fy 是一个轻量级、可扩展的基于 fetch 的 HTTP 请求库, 适用于浏览器和 Node.js 环境

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors