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

modern-fetch

v1.4.2

Published

一个基于fetch api封装的restful请求风格的简易http请求库

Readme

modern-fetch

一款基于 fetch API 轻封装的 HTTP 请求库,专注于 RESTful API 风格调用,零依赖,源码简洁易懂。

适用于:浏览器、Node.js >= 18、Bun、Deno、React Native。


安装

npm install modern-fetch --save

类型定义

type Methods = 'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE';

type DataType = RequestInit['body'] | Record<any, any> | number;

type HeaderType = Record<string, string>;

type IFetchOption = Omit<RequestInit, 'body' | 'method' | 'headers'>;

type ResponseType = 'json' | 'text' | 'formData' | 'blob' | 'arrayBuffer' | undefined;

type IRequestInit = RequestInit & { headers: Headers };

type RequestOption = {
  headers?: HeaderType;
  fetchOptions?: IFetchOption;
  responseType?: ResponseType;
  data?: DataType;
};

type ReqInterceptor = (requestInit: IRequestInit, url: string) => Promise<IRequestInit>;

type ResInterceptor = <T = any>(
  response: Response,
  responseType: ResponseType,
  retry: () => Promise<T>
) => Promise<any>;

type ErrInterceptor = (err: any) => void;

type Transform = (data: any, method?: Methods, url?: string) => any;

interface IFactoryOption {
  headers?: HeaderType;
  fetchOptions?: IFetchOption;
  reqIntcp?: ReqInterceptor;
  resIntcp?: ResInterceptor;
  errIntcp?: ErrInterceptor;
  transform?: Transform;
}

快速开始

1. 创建实例

import ModernFetch from 'modern-fetch';

const CommonHttp = new ModernFetch({
  baseUrl: 'https://api.example.com',
  prefix: 'v1',
  headers: {
    'Accept': 'application/json',
  },
  fetchOptions: {
    credentials: 'include',
  },
});

| 参数 | 类型 | 说明 | |------|------|------| | baseUrl | string | 基础请求地址 | | prefix | string | 请求路径前缀 | | headers | HeaderType | 公共请求头 | | fetchOptions | IFetchOption | 原生 fetch 配置(剔除 body/method/headers) | | reqIntcp | ReqInterceptor | 实例请求拦截器 | | resIntcp | ResInterceptor | 实例响应拦截器 | | errIntcp | ErrInterceptor | 实例错误拦截器 | | transform | Transform | 请求参数转换函数 |

2. 添加拦截器

请求拦截器 — 在请求发送前修改配置

CommonHttp.addReqIntcp(async (requestInit, url) => {
  requestInit.headers.set('Authorization', 'Bearer xxxxx');
  return requestInit;
});

响应拦截器 — 统一处理响应数据

CommonHttp.addResIntcp(async (response, responseType, retry) => {
  if (response.ok) {
    switch (responseType) {
      case 'json':
        return await response.json();
      case 'text':
        return await response.text();
      case 'blob':
        return await response.blob();
      case 'formData':
        return await response.formData();
      case 'arrayBuffer':
        return await response.arrayBuffer();
      default:
        return response;
    }
  }

  // 401 场景:刷新 token 后重试
  if (response.status === 401) {
    await refreshToken();
    return await retry();
  }

  return Promise.reject(response);
});

错误拦截器 — 统一处理网络/请求错误

CommonHttp.addErrIntcp((err) => {
  console.error('请求失败:', err.message);
});

请求参数转换器 — 在请求拦截器之前运行

CommonHttp.addTransform((data, method, url) => {
  // 例如:将所有数字转为字符串
  return data;
});

3. 请求数据变换 (Transform)

transform 是在 reqIntcp 之前执行的函数,用于统一转换请求参数:

// 示例:将日期对象转为时间戳字符串
CommonHttp.addTransform((data, method, url) => {
  if (data && data.date && data.date instanceof Date) {
    data.date = data.date.toISOString();
  }
  return data;
});

全局拦截器

多个实例共享同一套拦截逻辑时,可注册全局拦截器。当实例拦截器全局拦截器同时存在时,优先执行实例拦截器,全局拦截器不执行。

ModernFetch.addGlobalReqIntcp(async (requestInit, url) => {
  requestInit.headers.set('X-Global', 'true');
  return requestInit;
});

ModernFetch.addGlobalResIntcp(async (response, responseType, retry) => {
  // 全局响应处理
  return response;
});

ModernFetch.addGlobalErrIntcp((err) => {
  // 全局错误处理
});

创建请求对象

通过 create(url) 创建基于路径的请求对象,返回的实例包含 getpostputpatchdelete 方法。

const PostApi = CommonHttp.create('/posts');

请求方法调用规则

每个请求方法接收两个参数:

  • 第一个参数 data?: DataType
    • 若为普通对象,则作为请求参数(GET 会拼接到 URL 查询字符串,其他方法作为 body)
    • 若为字符串,则追加到 URL 路径末尾
    • 若为 FormData / Blob / ArrayBuffer,直接作为 body
  • 第二个参数 option?: RequestOption
// GET /v1/posts
PostApi.get();

// GET /v1/posts?id=1
PostApi.get({ id: 1 });

// GET /v1/posts/hot?id=1
PostApi.get('hot', { data: { id: 1 } });

// POST /v1/posts   body: { title: 'hello' }
PostApi.post({ title: 'hello' });

// POST /v1/posts/hot
PostApi.post('hot');

// POST /v1/posts/hot   body: { id: 1 }
PostApi.post('hot', { data: { id: 1 } });

// POST /v1/posts  FormData 上传
PostApi.post(new FormData());

// POST /v1/posts/file  Blob 上传
PostApi.post('file', { data: new Blob() });

// POST /v1/posts  ArrayBuffer
PostApi.post(new ArrayBuffer(0));

// 文件下载: GET /v1/posts/pic.jpg
PostApi.get('pic.jpg', { responseType: 'blob' });

请求方法一览

| 方法 | 说明 | |------|------| | get<R>(data?, option?) | 条件查询,参数拼接到 URL 查询字符串 | | post<R>(data?, option?) | 新增/提交 | | put<R>(data?, option?) | 全量更新 | | patch<R>(data?, option?) | 部分更新 | | delete<R>(data?, option?) | 删除 |


自定义 URL 请求

request 方法绕过 baseUrlprefix,直接请求指定 URL:

PostApi.request('https://other-api.com/endpoint', {
  method: 'POST',
  body: JSON.stringify({ key: 'value' }),
  headers: { 'Content-Type': 'application/json' },
}, 'json');

API 参考

class ModernFetch

| 实例方法 | 说明 | |----------|------| | addReqIntcp(interceptor) | 添加实例请求拦截器 | | addResIntcp(interceptor) | 添加实例响应拦截器 | | addErrIntcp(interceptor) | 添加实例错误拦截器 | | addTransform(transform) | 添加请求参数转换函数 | | create(url?) | 创建基于路径的 Request 对象 |

| 静态方法 | 说明 | |----------|------| | ModernFetch.addGlobalReqIntcp(interceptor) | 添加全局请求拦截器 | | ModernFetch.addGlobalResIntcp(interceptor) | 添加全局响应拦截器 | | ModernFetch.addGlobalErrIntcp(interceptor) | 添加全局错误拦截器 |

class Request

| 方法 | 说明 | |------|------| | get<R>(data?, option?) | GET 请求 | | post<R>(data?, option?) | POST 请求 | | put<R>(data?, option?) | PUT 请求 | | patch<R>(data?, option?) | PATCH 请求 | | delete<R>(data?, option?) | DELETE 请求 | | request<R>(url, requestInit, responseType?) | 自定义 URL 请求(不受 baseUrl/prefix 影响) |


参考链接