# Node fetch / axios

Node 中纯 HTTP 请求（不用无头浏览器）的两大主流栈。

## 现代：`undici`（内置 fetch）

Node 18+ 的 `fetch` 来自 `undici`。要走 HTTP-CONNECT 代理需 `undici` 的 `ProxyAgent`：

```bash
npm i undici
```

```js
import { fetch, ProxyAgent } from "undici";

const agent = new ProxyAgent(
  "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);

const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
console.log((await res.text()).trim());
console.log("Exit IP:", res.headers.get("x-helodata-exit-ip"));
```

ISP 把 URL 改为 `http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000`。

## SOCKS5 + undici

```bash
npm i socks-proxy-agent
```

```js
import { SocksProxyAgent } from "socks-proxy-agent";
import { fetch } from "undici";

const agent = new SocksProxyAgent(
  "socks5://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);
const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
```

## axios

```bash
npm i axios https-proxy-agent
```

```js
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent(
  "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);

const res = await axios.get("https://ipv4.icanhazip.com", {
  httpAgent: agent,
  httpsAgent: agent,
  proxy: false,                            // 关闭 axios 自带的代理逻辑
});
console.log(res.data.trim());
```

> 调用时务必 `proxy: false`，否则 axios 会用 `env` 推断又一次代理，干扰你设的 agent。

## ISP 轮换

```js
import fs from "node:fs";
import { fetch, ProxyAgent } from "undici";

const lines = fs.readFileSync("isp.txt", "utf8").trim().split("\n");
let i = 0;

async function fetchOne(url) {
  const [ip, port, user, pass] = lines[i++ % lines.length].split(":");
  const agent = new ProxyAgent(`http://${user}:${pass}@${ip}:${port}`);
  return fetch(url, { dispatcher: agent });
}
```

## 粘性会话（住宅）

```js
import crypto from "node:crypto";

function sessionUrl(country = "us") {
  const sid = crypto.randomBytes(4).toString("hex");
  const user = `helo_s1a2b3c4d5e-type-res-region-${country}-session-${sid}-sesstime-10`;
  return `http://${user}:PASSWORD@gate.helodata.io:7777`;
}
```

## 验证

```js
const ip = (await (await fetch("https://ipv4.icanhazip.com", { dispatcher: agent })).text()).trim();
console.log(ip);
```

## 常见陷阱

* **`undici` 的 `HTTP_PROXY` 环境变量模式** — 1.x 之前不识别环境变量，请显式用 `ProxyAgent`。
* **axios + `proxy: { host, port }`** — 这种写法**不**走 HTTP-CONNECT，会改写请求并破坏 HTTPS。请总用 `httpAgent`/`httpsAgent`。
* **连接复用** — `undici.Agent`（默认 dispatcher）会池化连接；每请求新建 `ProxyAgent` 会失去池。ISP 场景每个 IP 共享一个 agent。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.helodata.com/helodata-zh/ji-cheng-zhi-nan/pa-chong-gong-ju/node-fetch.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
