跳至内容
返回博客

5种 node-fetch 替代方案对比:Native Fetch、Axios、Got、Ky 和 SuperAgent

Raluca Penciuc最后更新于 4 min read
5种 node-fetch 替代方案对比:Native Fetch、Axios、Got、Ky 和 SuperAgent
简而言之:在支持的 Node.js 版本中,建议优先使用原生 Fetch,仅当需要替换具有实质意义的自定义代码时才添加依赖项。Axios 侧重于跨运行时的便捷性,Got 提供更深入的 Node.js 特定控制,Ky 提升了 Fetch 的易用性,而 SuperAgent 则适合流畅的表单和文件处理工作流。 无论您选择哪种方案,在迁移过程中都要保留状态、超时、重试、Cookie 以及流的行为。

node-fetch 是一个轻量级的 Node.js 包,提供与 Fetch 兼容的接口用于发起 HTTP 请求。开发者在比较 node-fetch 的替代方案时,通常会在三种路径中做出抉择:保留现有包、移除它转而使用运行时的全局 Fetch API,或者采用功能更丰富的 Node.js HTTP 客户端。

这一决策与其说是寻找一个通用的优胜者,不如说是将请求语义与您的工作负载相匹配。一个小型 API 客户端可能仅需 fetch()、显式状态检查和 JSON 解析。而生产环境集成则可能受益于拦截器、分阶段超时、重试策略、代理或代理控制、Cookie 存储、上传辅助工具或可预测的流式处理行为。

本指南对比了五种值得信赖的选项:原生 Fetch、Axios、Got、Ky 和 SuperAgent。它将内置功能与附加组件及应用程序代码区分开来,并指出了迁移过程中行为通常会发生变化的环节。 您还将获得一份“答案优先”的候选名单、兼容性与功能对比表、配套的迁移代码,以及基于场景的决策指南。由于包依赖和默认设置会发生变化,与时间相关的版本声明均已标注需核实,而非作为永久事实呈现。

快速答案:按用例划分的最佳 node-fetch 替代方案

在 node-fetch 的替代方案中,并不存在放之四海皆准的优选方案。请首先明确您的应用程序实际需要的功能,然后核实已安装的主版本及 Node.js 基础版本。

使用场景

最佳首选方案

原因

具有基本 HTTP 需求的新型服务

原生 Fetch

无客户端依赖,熟悉的 Fetch 语义

浏览器和服务器端代码共享

Axios

请求转换、实例、拦截器

仅限 Node.js 的集成,支持精细控制

Got

分阶段超时、钩子、流、重试控制

采用Fetch风格的代码,减少冗余代码

Ky

兼容Fetch的输入参数及便捷选项

表单、多部分上传和可链式调用的方法

SuperAgent

流畅式 API 和面向文件的辅助函数

这些选项可分为三类:运行时 API、Fetch 封装器以及完整的 HTTP 客户端。请在相应类别内进行比较,而非将每个功能缺失都视为缺陷。

是否应该替换 node-fetch?

将这一决策视为“保留”、“移除”或“替换”。当 node-fetch 的当前行为已通过测试覆盖、运行时限制支持其继续使用,且迁移无法带来具体的运营效益时,请保留 node-fetch。 当您支持的 Node.js 版本已提供全局 Fetch 接口,且您的代码仅需标准风格的请求时,应移除它。当重试、钩子、分阶段超时、集中式转换或上传辅助函数正逐渐成为应用程序维护的基础设施时,应替换它。

在维护现有集成时,关于如何使用 node-fetch 进行 HTTP 请求的实用指南仍然很有用。仅因较新的运行时包含 Fetch,并不意味着该包自动就是错误的选择。关键问题在于,其他选项能否在不悄然改变语义的前提下降低风险或精简代码。

原生 Fetch 与其他依赖项的对比

原生 Fetch 可缩小依赖范围,并使服务器代码与标准化的 Request、Response、Headers 和 Body 模型保持一致。官方 Node.js 全局 Fetch 文档记录了其版本和稳定性历史,您应根据生产环境中部署的具体运行时进行核对。

不要依赖于假设的平台超时。应使用 AbortController 设置显式的截止时间,或者在 AbortSignal.timeout() 仅在确认该 API 存在于所有受支持的 Node.js 版本之后才使用。

ESM、CommonJS 及受支持的 Node.js 版本

捕获的 node-fetch 文档说明 v3 仅支持 ESM,而 v2 兼容 CommonJS。该文档还列出了 v3 的打包 TypeScript 声明以及更低的 Node.js 最低支持版本,但这些细节和维护指南具有时效性。在确定采用其中任何一个版本之前,请确认包元数据。

对于 Axios、Got、Ky 和 SuperAgent 也应采取同样的做法。请查阅 engines, type, exports,以及 types 使用 npm view <package> engines type exports types,随后在持续集成(CI)环境中测试实际的导入形式。当前各主要版本在 ESM 支持、CommonJS 互操作性以及运行时要求方面可能存在差异。

一目了然地比较这五种 node-fetch 替代方案

将其作为候选清单。B 表示内置功能,A 表示附加组件或适配器,M 表示手动应用代码。星号标记的是受时间限制的功能,需要针对具体版本进行验证。

客户端

最佳匹配

运行时/模块

JSON / 状态

截止时间 / 取消

重试

钩子

代理/代理程序

Cookies

流/上传

HTTP/2

node-fetch

现有的 Fetch 代码

Node;v2 CJS,v3 ESM*

M / M

M / B 信号

M

M

B 选项*

M-A

B 节点流、FormData

M

原生 Fetch

最小依赖

支持的 Node*

M / M

M / B 信号*

M

M

M 或运行时钩子*

M-A

B Web 流、FormData

M 或运行时*

Axios

跨运行时便利性

Node/浏览器;导出*

B / B 错误

B / B 信号

A

B 拦截器

B-A*

M-A

B-A*

A*

已获取

节点服务控制

节点;当前主专业 ESM*

B / B 误差

B / B*

B*

B

B 代理*

A*

B 流

B*

Ky

Fetch 人体工学

数据提取运行时;ESM*

B / B 错误

B* / B 信号

B*

B

通过Fetch获取M

M-A

B 获取流/FormData

M 或运行时*

SuperAgent

表单和文件

Node/浏览器;导出*

B / B 错误*

B / 中止 API*

B*

A 插件*

B-A*

B-A 代理*

B

A*

切换客户端前需考虑的关键因素

功能列表最长通常并非最佳标准。请明确请求契约:请求体编码、状态码错误、时限、重试策略以及传递给下游的流类型。只有当这些行为始终符合预期时,在各种节点获取方案中进行选择才是安全的。

本指南不包含速度排名、下载量、星级评分、包大小宣称以及维护排行榜。若缺乏最新的测量数据、日期及明确的测试方法,这些数字只会制造虚假的精确度,而非支持合理的工程决策。

JSON、状态错误、超时和重试

Fetch 风格的客户端通常需要显式 JSON.stringify()、内容标头、响应解析以及 response.ok 校验。Axios 会转换对象有效载荷,将解析后的内容暴露在 response.data,并默认拒绝非2xx响应,除非 validateStatus 未更改该规则。这一差异可能导致代码从普通分支移入 catch.

无论客户端如何,都要显式设置时限。区分总请求时限与连接、TLS、首字节和套接字阶段的时限。重试也需要制定书面策略。在瞬时失败后重试 GET 请求,与重放可能已被提交的 POST 请求是不同的。将自动重试支持视为策略引擎,而非简单的勾选框。

对于 Node.js 服务和数据抓取,传输细节通常决定了客户端的类型。代理支持可能来自客户端选项、HTTP 代理、Fetch 分发器或适配器。Cookie 持久化通常需要 JAR 文件或应用程序逻辑,因为服务器端客户端无法继承浏览器的 Cookie 存储。

检查响应正文是否为 Node.js Readable 流还是 WHATWG ReadableStream 对象。在更改下载管道之前,请先验证多部分 FormData 的行为、文件大小限制、重定向处理、解压以及反压机制。HTTP/2 支持可能来自客户端、扩展程序或底层运行时。

当这些传输问题成为迁移的主要考量时,一份关于 node-fetch 中代理配置的实用指南,以及一份关于使用 JavaScript 和 Node.js 进行网络爬虫的更全面指南,将是不可或缺的参考资料。

原生 Fetch:零依赖的基准

对于受支持的运行时环境,原生 Fetch 应作为评估其他 node-fetch 替代方案的首要基准。它保留了熟悉的基于 Promise 的接口,无需外部客户端包,同时也保留了 Fetch 刻意设计的显式特性:你需要自行序列化 JSON、解析响应正文,并自行判断 HTTP 错误的含义。

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5_000);

try {
  const response = await fetch('https://api.example.com/items', {
    signal: controller.signal
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  const items = await response.json();
  console.log(items);
} finally {
  clearTimeout(timer);
}

此示例设置了自己的超时时间,而非假设某个未记录的默认值。网络故障和取消操作会拒绝该 Promise,而 404 或 500 状态码仍会返回一个 Response 对象,你的代码必须对其进行检查。这种行为遵循了更广泛的 Fetch 标准

迁移过程中的主要陷阱在于流。node-fetch 提供 Node.js 可读的响应正文,而原生 Fetch 则遵循 Web 流语义。如果现有代码将数据直接传递到 stream.pipeline,请适配或转换请求主体,并针对反压、取消和错误传播进行回归测试。

Axios:在 Node.js 和浏览器间实现便捷性

当需要在浏览器和服务器端代码中集中管理便利性时,Axios 是 Node.js Fetch 的强有力替代方案。将普通对象作为请求数据传递会触发 JSON 序列化和相应的内容处理,解析后的响应数据可通过 response.data获取。默认情况下,非 2xx 状态码将转换为错误,服务器详细信息存储在 error.response.

可重用的实例可让您标准化基础 URL、头部、超时和 validateStatus。拦截器是内置的中间件,用于身份验证、追踪、日志记录、刷新流程和响应转换。这可以替代重复的封装代码,但也可能隐藏行为,因此请确保对拦截器顺序和失败路径进行测试。

在捕获的证据中,重试功能不属于 Axios 核心功能。请通过扩展或应用程序策略添加重试功能,并验证符合条件的方法。代理行为可能取决于协议、环境变量、适配器或自定义代理,因此请测试确切的部署路径,而非假设一种代理选项能涵盖所有情况。

一份专门的 Axios 代理配置指南和 Axios 头部处理手册是很有用的内部后续参考资料。在提交之前,还应确认当前包的 Node.js 最低支持版本、模块导出、取消 API、流行为以及任何 HTTP/2 适配器。

Got:针对 Node.js 服务的精细控制

在这一系列 node-fetch 替代方案中,Got 是最专注于 Node.js 的选项。其价值在于控制力:独立的超时阶段可分别覆盖 DNS 查询、连接、TLS 协商、请求发送、首次响应字节、套接字闲置,或整个生命周期。这使得故障遥测比单一的通用超时更具可操作性。

Got 还提供了适用于服务间集成的钩子和流式 API。相关文档描述了带退避机制的自动重试、对 Retry-After,以及原生 HTTP/2 支持,但必须针对已安装的版本验证默认设置、适用方法及当前传输行为。切勿在没有幂等策略的情况下,让默认重试策略重发会改变状态的请求。

对于数据抓取或出站 API 网关,需检查代理配置、代理路由、Cookie-Jar 集成、解压以及流限制。Got 更丰富的选项界面虽可消除自定义基础设施,但会增加配置责任。建议优先使用经过审查的共享实例,而非让选项随每次调用而泛滥。

当前版本通常被文档描述为面向 ESM 且仅支持 Node.js。在选择 Got 作为 CommonJS 服务或旧版运行时之前,请确认 engines、导出项、捆绑类型及维护状态。

Ky:简化 Fetch 操作,减少冗余代码

Ky 介于原生 Fetch 和完整的 Node.js HTTP 客户端之间。它支持 Fetch 风格的输入,同时添加了方法快捷方式、可复用实例、钩子以及请求选项,从而减少重复代码。这使其在您喜欢 Fetch 语义但希望使用更精简的应用程序封装时颇具吸引力。

请查阅最新文档以了解确切的超时设置、重试默认值、支持的方法以及错误处理行为。现有记录显示,非 2xx 响应会被视为错误,且重试功能为内置行为,在替换 node-fetch 时,这两点都可能导致行为变化。请有意识地保留现有的状态分支,而非让便捷的默认设置来决定。

Ky 将重要的传输行为委托给底层的 Fetch 实现。因此,代理或分发器配置、Cookie、Web 流、FormData 以及 HTTP/2 部分取决于运行时环境。部分版本已记录下载进度,而上传进度支持则更为有限,因此在围绕这些功能设计遥测方案之前,请务必验证两者。

当 Fetch 兼容性比 Node 特有的传输控制更重要时,请在 node-fetch 的替代方案中选择 Ky。

SuperAgent:可链式请求与文件工作流

对于偏好流式 API(如 .get(), .set(), .send(), .field(),以及 .attach()。其表单和文件辅助函数使多部分工作流更易于理解,而内置的响应解析和进度导向型 API 则可简化上传或下载代码。

其取舍在于与 Fetch 的语义差异。错误处理、超时配置、重定向、取消操作以及请求体访问都需要制定全新的测试方案,而非简单地替换导入语句。原始资料中关于钩子和 AbortController 支持的说明存在矛盾。 请将插件视为附加组件,并确认您所选的版本是否使用其自身的中止方法、是否接受信号,或者是否需要包装器。

同样,在启用重试功能前,请先验证其行为及适用方法,且在缺乏最新文档的情况下,切勿默认 HTTP/2 已集成到核心中。在 Node.js 中,请针对您的具体工作流检查代理、Cookie 持久化及流的行为。

SuperAgent 最适合在以下情况下使用:当其可链式调用形式和文件 API 能够替代有实际意义的自定义代码时,而不仅仅是因为语法看起来简洁。

在不改变行为的情况下从 node-fetch 迁移

安全的迁移应从记录现有语义开始,然后逐层进行更改。若迁移到原生 Fetch,最小的行为保留式更改可能是移除导入语句,同时保留显式的 JSON 序列化、状态检查和取消操作。

// Before
import fetch from 'node-fetch';
await sendJson(fetch);

// After
await sendJson(globalThis.fetch);

async function sendJson(fetchImpl) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 5_000);

  try {
    const response = await fetchImpl('https://api.example.com/jobs', {
      method: 'POST',
      headers: {'content-type': 'application/json'},
      body: JSON.stringify({status: 'queued'}),
      signal: controller.signal
    });

    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } finally {
    clearTimeout(timer);
  }
}

在迁移至 Axios 或其他会抛出状态码的客户端时,请配置其状态策略,或有意识地重写周围的控制流。切勿让 404 状态码意外地从已处理的结果路径转移到通用重试路径中。

迁移检查清单:导入、请求体、错误、取消和流

  • 确认 ESM 或 CommonJS 导入方式以及部署的 Node.js 最低版本。
  • 比较 JSON、URL 编码、Blob、File 和 FormData 序列化方式。
  • 保留非 2xx 状态码的处理逻辑、重定向模式和限制,以及对绝对 URL 的假设。
  • 验证取消错误类型,并确认截止时间是否覆盖整个生命周期。
  • 请注意,已消耗的请求主体在未进行克隆或缓冲的情况下无法被重复读取。
  • 显式转换 Node 流和 Web 流,然后测试反压和部分失败情况。
  • 重新配置代理、代理程序或调度程序、Cookie 存储、解压缩以及响应大小限制。
  • 为上传、大文件下载、重试、重复 POST 保护以及中止后的清理添加回归测试。

根据项目场景选择

使用以下场景默认设置来筛选五种 node-fetch 替代方案:

  • 依赖项最少的现代服务:从原生 Fetch 开始。
  • 传统 CommonJS 应用程序:暂时保留 node-fetch v2,或选择当前 CommonJS 路径和 Node 最低兼容性已通过验证的客户端。
  • 浏览器与服务器代码共享:若 Fetch 兼容性比拦截器更重要,则先评估 Axios,再评估 Ky。
  • 重重试的 Node 集成:评估 Got,并采用明确的幂等性策略。
  • 表单、附件和上传进度:根据具体工作流评估 SuperAgent 或 Axios。
  • 基于代理的爬取:应根据代理或调度器的控制能力、Cookie、流处理以及阻塞管理需求进行选择,而不仅仅基于请求语法。

最终建议

在您实际支持的每个 Node.js 版本上,首先评估原生 Fetch。这是判断其他依赖项是否值得采用的最明确基准。

若需跨运行时便利性,请选择 Axios;若需深度控制 Node.js,请选择 Got;若需 Fetch 风格的操作体验,请选择 Ky;若需可链式连接的表单和文件工作流,请选择 SuperAgent。最佳的 Node.js Fetch 替代方案,应是其经过验证的内置功能能够替代有意义的自定义代码,同时保持您的请求契约不变。

关键要点

  • 当所有已部署的 Node.js 版本均支持原生 Fetch,且您仅需明确、符合标准的 HTTP 行为时,请优先评估原生 Fetch。
  • 选择依赖库应着眼于经过验证的便利性——即能消除自定义代码,而非功能清单最长的那个。
  • 通过回归测试,确保 JSON 编码、非 2xx 状态处理、超时、取消、重定向和重试语义得以保留。
  • 将代理、Cookie 存储、流类型、文件上传和 HTTP/2 视为传输层问题,可能需要代理、适配器或插件来处理。
  • 在依赖模块格式、Node.js 的最低要求或默认值之前,请先检查当前包的元数据和官方文档。

常见问题

在 CommonJS 项目中,我可以继续使用 node-fetch v2 而不进行迁移吗?

可以,前提是它仍与您所支持的运行时、安全策略及维护预期兼容。请锁定版本,审阅当前项目指南,并通过测试确保请求行为得到覆盖。将其视为一项明确的兼容性决策,而非永久默认方案。若未来因 Node.js 升级、依赖项策略变更或出现不受支持的传递性包,导致继续使用成本过高,请规划退出方案。

大多数服务器端客户端都需要显式处理 Cookie 或集成 Cookie 存储库。 原生 Fetch 和 node-fetch 的行为与浏览器 Cookie 存储不同。其他客户端可能与 Cookie 存储、代理或插件集成,而在某些版本中,持久的 SuperAgent 代理可能会有所帮助。在依赖会话持久性之前,请验证域名、路径、过期时间、重定向以及并发请求的行为。

自动重试是否应适用于 POST 及其他非幂等请求?

不,默认情况下不适用。即使客户端从未收到响应,超时的 POST 请求也可能已到达服务器。仅当操作设计为支持重试时才应重试,通常需配合幂等性键、服务器端去重规则或安全的应用程序特定契约。此外,应限制重试次数,并在适当情况下遵守服务器的退避信号。

使用原生 Fetch 而不是 node-fetch 流式传输大型响应时,会有哪些变化?

响应体通常会从 Node.js Readable 变为 WHATWG ReadableStream。现有的 .pipe()stream.pipeline() 代码可能因此需要转换,例如 Readable.fromWeb() 在支持的情况下,或采用 Web 流管道。请测试背压、中止传播、部分文件、解压和清理,因为小缓冲区测试的成功结果可能无法揭示生产环境中的流式传输故障。

结论

在各种 node-fetch 替代方案中做出正确选择,取决于您希望保留哪些行为,以及不再希望维护哪些基础设施。 对于受支持的运行时环境而言,原生 Fetch 是一个明智的基准选择,因为它在保留熟悉的 Fetch 语义的同时消除了依赖关系。Axios 提供了跨运行时的转换和拦截器,Got 强调对 Node.js 的精细控制,Ky 为 Fetch 提供了便捷的封装,而 SuperAgent 则使表单和文件工作流更易于阅读。

在切换之前,请全面盘点每个请求的相关约定。检查导入项、JSON 序列化、非 2xx 状态码处理、超时、取消、重试条件、重定向、Cookie 持久化、代理路由、文件上传以及流类型。然后,针对您计划安装的具体主版本,验证当前包的依赖项和默认设置。

对于数据抓取任务,HTTP 客户端可能只是问题的一层。如果阻塞、验证码和代理轮换所耗费的精力超过了响应处理,请考虑使用 WebScrapingAPI 提供的 Scraper API。它会在处理该请求层的同时返回原始 HTML。 将解析和业务逻辑保留在您的应用程序中,并选择能最清晰地处理这些剩余职责的客户端。

关于作者

Raluca Penciuc, 全栈开发工程师 @ WebScrapingAPI

Raluca Penciuc

全栈开发工程师

Raluca Penciuc 是 WebScrapingAPI 的全栈开发工程师,主要负责开发爬虫、优化规避机制,并探索可靠的方法以降低在目标网站上的被检测概率。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。