Promise based HTTP client for the browser and node.js

Fetch-Go is a lightweight, Axios-compatible HTTP client built on native fetch() with HTTP/2 support, smart interceptors, and built-in retry. Drop-in replacement in a ~6KB package.

import fetchgo from 'fetch-go';

// Auto-detects Node.js vs Browser
const { data } = await fetchgo.get('/users');
// data is auto-parsed JSON

Features

Browser & Node.jsAUTO

Auto-detects environment: native fetch() in browsers, http/https adapter in Node.js. No manual config needed.

Smart InterceptorsNEW

Intercept requests and responses with runWhen conditional filtering and synchronous mode for zero-overhead processing.

Retry Built-in

Exponential backoff retry with configurable delays, status codes, and custom conditions. No plugins needed.

{}

Auto JSON

Automatically serializes request bodies and parses JSON responses. Zero boilerplate.

Cancellation

Native AbortController support. No deprecated CancelToken — just standard APIs.

TypeScript First

Full generic support. get<User[]>('/users') returns properly typed data.

Advanced FormsNEW

Auto-convert to FormData or URLSearchParams with dots, indexes, and metaTokens options.

🔒

XSRF Protection

Reads XSRF tokens from cookies and sends them as headers automatically.

Progress + Rate LimitNEW

Upload/download progress events with maxRate throttling for bandwidth control.

HTTP/2

Native HTTP/2 support in Node.js via httpVersion: 2. Multiplexed streams with full feature support.

📋

Per-Method HeadersNEW

Set default headers per method: common, get, post, etc. Just like Axios.

TransitionalNEW

silentJSONParsing, forcedJSONParsing, clarifyTimeoutError for fine control.

🔑

Basic Auth

Built-in auth config for HTTP Basic Auth. Auto-encodes to Authorization header.

🌐

Proxy Support

Node.js proxy with auth, plus custom httpAgent/httpsAgent and socketPath.

🔗

Static HelpersNEW

all(), spread(), isCancel() — full Axios-compatible utility methods.

All Features

Comparison

Feature fetch() Axios Fetch-Go
Bundle size 0KB ~13KB ~6KB
Auto JSON
Error on 4xx/5xx
Timeout ✓ native
Interceptors ✓ runWhen
Retry ✗ plugin ✓ built-in
Cancel Manual CancelToken ⚠️ ✓ AbortController
Form serialization ✓ advanced
XSRF protection
Upload progress ✓ stream
Rate limiting ✓ maxRate
Per-method headers
Env auto-detect
Transitional
Max redirects
Node.js adapter ✓ default ✓ auto
TypeScript Manual ✓ generics
HTTP/2 ✓ native
Basic Auth
Proxy
Based on XMLHttpRequest native fetch()

Installing

Using npm:

$ npm install fetch-go

Using yarn:

$ yarn add fetch-go

Examples

GET request

import fetchgo from 'fetch-go';

// GET with typed response
const { data } = await fetchgo.get<User[]>('/api/users');

// With query params
const { data } = await fetchgo.get('/api/users', {
  params: { page: 1, limit: 10 }
});

POST request

const { data } = await fetchgo.post('/api/users', {
  name: 'John',
  email: 'john@example.com'
});

Per-method headers

const api = fetchgo.create({
  baseURL: 'https://api.example.com',
  headers: {
    common: { 'Accept': 'application/json' },
    get:    { 'Cache-Control': 'no-cache' },
    post:   { 'Content-Type': 'application/json' },
  }
});

Smart Interceptors

api.interceptors.request.use(
  (config) => {
    config.headers['X-Auth'] = getToken();
    return config;
  },
  undefined,
  {
    // Only run for /api/ routes
    runWhen: (config) => config.url?.startsWith('/api/'),
    // Run synchronously (no await)
    synchronous: true,
  }
);

Rate Limiting

// Limit download to 100 KB/s
await fetchgo.get('/large-file', {
  maxRate: 100 * 1024,
  onDownloadProgress: (e) => {
    console.log(`${Math.round(e.progress * 100)}%`);
  }
});

// Separate upload/download rates
await fetchgo.post('/upload', file, {
  maxRate: [50 * 1024, 200 * 1024], // [upload, download]
});

Advanced FormData

import { toFormData } from 'fetch-go';

// Dot notation for nested keys
toFormData({ user: { name: 'John' } }, undefined, { dots: true });
// → user.name = 'John'

// Array index modes
toFormData({ tags: ['a', 'b'] }, undefined, { indexes: true });
// → tags[0]=a, tags[1]=b

Parallel Requests

// fetchgo.all() + fetchgo.spread()
const [users, posts] = await fetchgo.all([
  fetchgo.get('/api/users'),
  fetchgo.get('/api/posts'),
]);

Transitional Options

const api = fetchgo.create({
  transitional: {
    silentJSONParsing: true,    // Don't throw on bad JSON
    forcedJSONParsing: false,   // Force JSON parse
    clarifyTimeoutError: true, // ETIMEDOUT code
  }
});

Error Handling

import { FetchGoError, isCancel } from 'fetch-go';

try {
  await api.get('/might-fail');
} catch (error) {
  if (fetchgo.isCancel(error)) {
    console.log('Cancelled!');
  } else if (error instanceof FetchGoError) {
    console.log(error.status);       // 404
    console.log(error.response?.data);// { message: "Not Found" }
    console.log(error.code);         // "ERR_BAD_REQUEST"
  }
}

Migrate from Axios

// Just change the import — everything else works!
- import axios from 'axios';
+ import fetchgo from 'fetch-go';

- const api = axios.create({ baseURL: '...' });
+ const api = fetchgo.create({ baseURL: '...' });

const { data } = await api.get('/users');