npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tiyee/requests

v1.1.2

Published

借鉴 Python requests 调用方式的轻量 TypeScript fetch 封装

Readme

@tiyee/requests

借鉴 Python requests 的简洁调用方式,支持独立位置参数和配置对象,使用 JavaScript 的 async/await、原生 Response 和 AbortSignal。提供 Axios 风格的请求与响应拦截器,无运行时依赖,包含 ESM、CommonJS 和 TypeScript 类型声明。

运行环境:现代浏览器或 Node.js 18.17+。同时使用 timeout 和 signal 时需要原生 AbortSignal.any。

安装

pnpm add @tiyee/requests
# 或
npm install @tiyee/requests

浏览器中可以使用相对 URL;Node.js 中需要完整 URL,或通过实例的 prefix 配置完整域名。以下 /api/... 示例适用于浏览器,也可在 Node.js 中替换成完整 URL。

import {create} from '@tiyee/requests'

// Node.js / ESM
const api = create({prefix: 'https://example.com/api'})
const response = await api.get('/users')
console.log(await response.json())

CommonJS 使用默认导出的 default 属性,也支持具名导出:

const {default: requests} = require('@tiyee/requests')

requests.get('https://example.com/api/users')
    .then(response => response.json())
    .then(users => console.log(users))
    .catch(error => console.error(error))

快速开始

import requests from '@tiyee/requests'

const api = requests.create({
    prefix: '/api',
    timeout: 5000,
    throwHttpErrors: true,
})

api.interceptors.request.use(config => {
    config.headers.set('Authorization', 'Bearer token')
    return config
})

// 独立参数顺序:url, params, data, json, headers, options
const response = await api.get('/users', {page: 1, tag: ['js', 'ts']})
console.log(response.status, response.ok)
console.log(response.headers.get('content-type'))
const users = await response.json()

await api.post('/users', {source: 'web'}, undefined,
    {name: 'Alice'}, {'X-Request-ID': '123'})

// 配置对象用法也支持
await api.patch('/users/1', {json: {name: 'Bob'}})
await api.delete('/users/1')

支持 get、post、put、patch、delete、head、options,也可以使用 requests.request('POST', url, options)。

独立参数

所有请求方法也接受独立位置参数,顺序一致:

requests.get(url, params, data, json, headers, options)
requests.post(url, params, data, json, headers, options)
requests.put(url, params, data, json, headers, options)
requests.patch(url, params, data, json, headers, options)
requests.delete(url, params, data, json, headers, options)
requests.head(url, params, data, json, headers, options)
requests.options(url, params, data, json, headers, options)
requests.request(method, url, params, data, json, headers, options)

| 参数 | 类型 / 用途 | 省略时的行为 | | --- | --- | --- | | url | 请求地址,必填 | 与实例的 prefix、suffix 直接拼接 | | params | 对象、URLSearchParams 或查询字符串 | 沿用配置中的查询参数 | | data | 原生 BodyInit、对象或数组 | 沿用配置中的请求体 | | json | JSON 可序列化值,支持 null、false、0 等值 | 沿用配置中的 JSON 请求体 | | headers | HeadersInit:对象、Headers 或键值对数组 | 沿用配置中的请求头 | | options | 最后的额外配置对象,如 timeout、signal、responseType | 使用实例配置和默认值 |

// 只传查询参数
const response = await requests.get('/api/users', {page: 1, tag: ['js', 'ts']})

// 查询参数 + JSON + 请求头;不使用的位置传 undefined
await requests.post('/api/users', {source: 'web'}, undefined,
    {name: 'Alice'}, {Authorization: 'Bearer token'})

// data + 最后的额外配置对象
const result = await requests.post('/api/login', undefined,
    new URLSearchParams({username: 'alice', password: 'secret'}),
    undefined, undefined, {timeout: 5000, responseType: 'json'})

位置参数会覆盖最后 options 对象中的对应字段,undefined 表示沿用配置。data 和 json 仍然互斥,GET/HEAD 仍然不接受请求体。

为兼容旧用法,只传一个对象且包含 params、headers、timeout 等配置字段时,会作为配置对象处理。查询参数与配置字段同名时,请显式补上第三个参数 undefined,例如 requests.get(url, {timeout: 123}, undefined)。

复用配置

import {create, extend} from '@tiyee/requests'

const api = create({
    prefix: 'https://example.com/api',
    headers: {Authorization: 'Bearer token'},
    params: {lang: 'zh'},
    timeout: 5000,
    throwHttpErrors: true,
})

const response = await api.get('/users', {
    headers: {'X-Request-ID': '123'},
    params: {page: 1},
})
// 请求 https://example.com/api/users?lang=zh&page=1
// 同时发送 Authorization 和 X-Request-ID

const legacy = extend({prefix: '/api'}) // extend 是 create 的别名

requests.create()、requests.extend() 也可使用。请求级配置覆盖实例配置;请求头按大小写不敏感的名称合并,查询参数按名称合并。请求级覆盖不会影响之后的请求。传入 params: {lang: null} 可移除实例中的 lang。

prefix、URL、suffix 按顺序直接拼接,不会自动补 /,完整 URL 也不会自动跳过 prefix。需要使用另一域名时,可在该请求的配置对象中设置 prefix: ''。

合并优先级为:实例配置 → 请求的配置对象 → 非 undefined 的独立位置参数。Headers 和 params 按键合并,其余配置直接覆盖。查询参数的数组使用重复键,null、undefined 和空数组可删除对应实例参数;URL 字符串上已有的查询参数会保留,新参数追加在其后。

请求体

// JSON:自动设置 Content-Type,保留调用方指定的请求头
await requests.post('/api/users', {json: {name: 'Alice'}})

// URL 编码表单:由 fetch 根据 URLSearchParams 设置 Content-Type
await requests.post('/api/login', {
    data: new URLSearchParams({username: 'alice', password: 'secret'}),
})

// 文件:由 fetch 生成 multipart boundary
const form = new FormData()
form.append('file', new Blob(['hello'], {type: 'text/plain'}), 'hello.txt')
await requests.post('/api/upload', {data: form})

// 兼容原有 data + requestType 用法
await requests.post('/api/users', {data: {name: 'Alice'}}) // 默认按 JSON 编码
await requests.post('/api/login', {
    data: {username: 'alice', password: 'secret'},
    requestType: 'form',
})

json 和 data 同时提供会抛出 TypeError。GET/HEAD 不接受请求体,请使用 params。字符串、Blob、二进制数据及其他原生 BodyInit 可通过 data 直接传入。

data 支持 ReadableStream,发送流式请求体时会自动设置 duplex: 'half';浏览器端仍需运行环境支持流式上传。json 和默认按 JSON 编码的 data 使用相同的序列化校验,循环引用、BigInt 或顶层 toJSON() 返回 undefined 都会抛出 TypeError。

响应与类型

默认返回原生 Response,使用 response.status、response.ok、await response.text()、await response.json()。响应体遵循 fetch 的一次性读取规则,重复读取前请先 clone()。

const text = await requests.get('/api/message', {responseType: 'text'}) // string
const blob = await requests.get('/api/file', {responseType: 'blob'}) // Blob
const data = await requests.get('/api/users', {responseType: 'json'}) // unknown

const textApi = requests.create({responseType: 'text'})
const message = await textApi.get('/api/message') // string
const raw = await textApi.get('/api/message', {responseType: 'response'}) // Response

responseType 支持 response(默认)、json、text、blob、arrayBuffer 和 formData。返回类型随配置推导;JSON 返回 unknown,请按接口约定校验或收窄类型。无效 JSON 保留原生解析错误。

显式传入 responseType 时返回对应类型;若配置变量中的 responseType 可省略,返回类型会包含实例默认类型。例如 RequestOptions<'text'> 传给默认实例时返回 string | Response。create()、extend() 和直接构造 RequestClient 同样会考虑配置缺省时的原生 Response 回退。

使用可省略 responseType 的配置变量创建实例时,建议使用 create() 保留精确推导;直接构造也可以显式写为 new RequestClient<'text' | 'response'>(options)。

| responseType | 返回值 | | --- | --- | | response | 原生 Response,可自行读取或处理流 | | json | unknown,需要校验或收窄 | | text | string | | blob | Blob | | arrayBuffer | ArrayBuffer | | formData | FormData |

自动解析结果直接返回,没有 Axios 的 {data, status, headers} 包装。需要同时读取状态、响应头和响应体时,请保留默认的 responseType: 'response'。

错误、取消与超时

import requests, {HTTPError, TimeoutError, raiseForStatus} from '@tiyee/requests'

try {
    const response = await requests.get('/api/users', {
        throwHttpErrors: true,
        timeout: 5000,
    })
    console.log(await response.json())
} catch (error) {
    if (error instanceof HTTPError) {
        console.log(error.response.status, await error.response.text())
    } else if (error instanceof TimeoutError) {
        console.log(error.timeout) // 毫秒
    } else {
        throw error
    }
}

// 默认不会因 HTTP 状态码抛错,也可以手动检查
const response = await requests.get('/api/users')
raiseForStatus(response) // 4xx/5xx 抛出 HTTPError

const controller = new AbortController()
const pending = requests.get('/api/users', {signal: controller.signal, timeout: 5000})
controller.abort()
await pending.catch(error => {
    if (error.name !== 'AbortError') throw error
})

timeout 单位是毫秒,默认 0 表示不超时。默认返回原生响应时,超时计时到响应头返回为止;使用自动解析的 responseType 时,计时包含响应体读取。用户取消保留原生错误或自定义 signal.reason,不会被误报成 TimeoutError。网络错误也保留 fetch 原始异常。

配置选项

| 选项 | 默认值 | 说明 | | --- | --- | --- | | params | 空查询 | 对象、查询字符串或 URLSearchParams;数组编码为重复键,null/undefined 忽略 | | headers | 空请求头 | 原生 HeadersInit,包括对象、Headers 和键值对数组 | | data | 无请求体 | BodyInit、对象或数组;对象/数组的编码由 requestType 决定 | | json | 无 JSON 请求体 | JSON 可序列化值;自动设置 Content-Type,与 data 互斥 | | requestType | json | data 中对象/数组的编码方式;form 表示 URL 编码表单 | | responseType | response | 返回原生 Response,或按指定类型自动解析 | | timeout | 0 | 毫秒;0 禁用超时,允许范围为 0~2147483647 | | signal | 无 | 外部 AbortSignal,可与 timeout 同时使用 | | throwHttpErrors | false | 为 true 时,4xx/5xx 抛出带原生 response 的 HTTPError | | prefix | 空字符串 | 在 URL 前直接拼接 | | suffix | 空字符串 | 在 URL 后、查询参数前直接拼接 | | credentials | same-origin | fetch 的凭据策略,也支持 omit / include | | mode | 由 fetch 决定 | fetch 的跨域策略;mod 是兼容旧名,mode 优先 | | redirect | follow | fetch 的 follow / manual / error | | method | 由调用方法确定 | get/post 等方法和 request 的首参决定方法;拦截器可修改 config.method |

浏览器 Cookie 和连接复用交给 fetch;实例用于复用配置,不实现独立 Cookie jar。浏览器的跨域和重定向可见性遵循 fetch 规则。

拦截器

提供与 Axios 拦截器 相似的 use(onFulfilled, onRejected)、eject(id) 和 clear()。默认导出的 requests 和每个 create/extend 实例各自维护拦截器,不会相互继承。

import requests, {HTTPError} from '@tiyee/requests'

const api = requests.create({prefix: '/api', throwHttpErrors: true})

// 请求:拿到合并后的配置,headers 为原生 Headers
const requestId = api.interceptors.request.use(async config => {
    config.headers.set('Authorization', 'Bearer token')
    return config
}, error => Promise.reject(error))

// 响应:拿到最终请求结果,或在失败分支统一处理错误
const responseId = api.interceptors.response.use(result => {
    if (result instanceof Response) console.log(result.status)
    return result
}, error => {
    if (error instanceof HTTPError && error.response.status === 401) {
        console.log('登录已失效')
    }
    return Promise.reject(error)
})

await api.get('/users', {page: 1})

api.interceptors.request.eject(requestId)
api.interceptors.response.eject(responseId)
api.interceptors.request.clear()
api.interceptors.response.clear()

请求拦截器按注册顺序逆序执行,响应拦截器按注册顺序执行。支持异步处理,返回值传给下一个拦截器;必须返回配置或结果才能继续保留它们。抛错或返回 rejected Promise 后,会进入后续拦截器的失败分支;同一次 use 的 onRejected 不捕获其配对 onFulfilled 的错误。失败分支返回正常值可恢复 Promise 链。

请求拦截器可修改 url、method、params、data、json、headers 和其他配置,在发送请求之前执行。响应拦截器在状态检查和自动解析之后执行:默认拿到原生 Response,配置 responseType: 'json' 时拿到解析后的 JSON。类型为 unknown,使用前请收窄。网络、超时、取消、配置校验、解析以及请求拦截器错误都会进入响应失败分支。

HTTP 状态错误默认进入响应成功分支;设置 throwHttpErrors: true 后,4xx/5xx 才进入失败分支。拦截器如果改变返回值类型或 responseType,调用方需要同步调整类型约定;方法的 TypeScript 返回类型仍按调用时的 responseType 推导。

拦截器链在请求启动时确定,之后注册或移除仅影响新请求。超时从发送请求开始计算,包含自动解析,不包含请求或响应拦截器执行时间。

仅注册失败处理时,第一个参数可以传 null。例如为 JSON 请求提供符合接口类型的降级结果:

import {create, HTTPError} from '@tiyee/requests'

const api = create({prefix: '/api', responseType: 'json', throwHttpErrors: true})

api.interceptors.response.use(null, error => {
    if (error instanceof HTTPError && error.response.status === 503) {
        return {items: [], unavailable: true}
    }
    return Promise.reject(error)
})

const result = await api.get('/users') // unknown,仍需按接口约定收窄

默认返回原生 Response 时,若拦截器需要读取内容但仍要交给调用方读取,请使用 await result.clone().json(),并返回原来的 result。调用方在请求返回后自行读取响应体所产生的错误,不会再进入已执行完毕的响应拦截器;需要统一处理解析错误时,请使用自动解析的 responseType。

与旧版的差异

  • 保留默认原生 Response、毫秒超时、extend、prefix、suffix、data 的默认 JSON 编码和所有 responseType。
  • 新增 json、create、通用 request、patch/head/options、throwHttpErrors 和具名错误类型。
  • 所有方法支持 params/data/json/headers 独立位置参数,配置对象用法继续保留。
  • 原 hooks 已移除,请使用 interceptors.response.use;请求准备逻辑使用 interceptors.request.use。响应拦截器处理自动解析后的结果,迁移时请按 responseType 调整读取方式。
  • 查询参数数组改为标准重复键(tag=a&tag=b),不再使用 tag[]=a&tag[]=b;复杂查询请自行序列化后传字符串或 URLSearchParams。
  • 实例与请求的请求头、查询参数现在会合并;空查询不会添加多余的 ?,参数会插入 URL hash 之前。
  • TypeScript 的自动 JSON 解析结果从 any 收紧为 unknown;原生 fetch 运行环境要求明确为 Node.js 18.17+ 或现代浏览器。

开发验证

pnpm --filter @tiyee/requests test

包括 ESM/CommonJS 构建、本地 HTTP 集成测试、返回类型推导检查,以及使用包导出入口的 Node16 模块模式 ESM/CommonJS 消费端编译检查。