⚡ Fetch-Go Documentation

Complete API reference for Fetch-Go v1.2 — a lightweight, Axios-compatible HTTP client built on native fetch().

Installation

$ npm install fetch-go
$ yarn add fetch-go
$ pnpm add fetch-go

Requirements: Node.js 18+ or any modern browser with fetch() support.

Quick Start

import fetchgo from 'fetch-go';

// GET — auto-parses JSON
const { data } = await fetchgo.get('/api/users');

// POST — auto-serializes body
const { data: user } = await fetchgo.post('/api/users', {
  name: 'John',
  email: 'john@example.com'
});

// TypeScript generics
interface User { id: number; name: string; }
const { data: users } = await fetchgo.get<User[]>('/api/users');

Create Instance

Create a pre-configured instance with defaults that apply to every request:

const api = fetchgo.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: { 'Authorization': 'Bearer token' }
});

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

Migrate from Axios

💡 Fetch-Go is a near drop-in replacement. Change the import and you're done.
// Before
import axios from 'axios';
const api = axios.create({ baseURL: '...' });

// After
import fetchgo from 'fetch-go';
const api = fetchgo.create({ baseURL: '...' });

// Everything else is the same!
const { data } = await api.get('/users');

HTTP Methods

MethodSignature
requestfetchgo.request(config)
getfetchgo.get(url[, config])
postfetchgo.post(url[, data[, config]])
putfetchgo.put(url[, data[, config]])
patchfetchgo.patch(url[, data[, config]])
deletefetchgo.delete(url[, config])
headfetchgo.head(url[, config])
optionsfetchgo.options(url[, config])
postFormfetchgo.postForm(url[, data[, config]])
putFormfetchgo.putForm(url[, data[, config]])
patchFormfetchgo.patchForm(url[, data[, config]])
getUrifetchgo.getUri(config)
createfetchgo.create(config)
allfetchgo.all(promises)
spreadfetchgo.spread(callback)
isCancelfetchgo.isCancel(error)

Config Options

PropertyTypeDefaultDescription
baseURLstringPrepended to url unless url is absolute
urlstringRequest URL (relative to baseURL)
methodstring'GET'HTTP method
headersobject{}Custom headers or per-method headers
paramsobjectURL query parameters
paramsSerializerfunctionCustom params serializer
dataanyRequest body
timeoutnumber0Timeout in ms (0 = no timeout)
signalAbortSignalCancel signal
responseTypestring'json'json|text|blob|arraybuffer|formdata|stream|document
validateStatusfunction200–299Resolve or reject based on status
retrynumber|bool|objectfalseRetry configuration
adapterstring|functionauto'fetch'|'http'|function
authobject{ username, password } for Basic Auth
withCredentialsbooleanfalseInclude cookies in cross-site requests
maxRedirectsnumber21Max redirects to follow
maxContentLengthnumberMax response size in bytes
maxBodyLengthnumberMax request body size in bytes
maxRatenumber|[up,down]Throttle bytes/sec
formSerializerstring|object'formdata'|'urlencoded'|{dots,indexes,metaTokens}
transitionalobjectBackward-compat options
envobjectPolyfill injection { FormData, Blob }
proxyobject|falseProxy config (Node.js only)
httpVersion1|21HTTP version (Node.js only)

Per-Method Headers NEW

Set different default headers for each HTTP method, just like Axios:

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

common headers are merged into every request. Method-specific headers are added only for that method.

Query Parameters

await fetchgo.get('/users', {
  params: { page: 1, limit: 10, tags: ['js', 'ts'] }
});
// → /users?page=1&limit=10&tags=js&tags=ts

// Custom serializer
await fetchgo.get('/users', {
  params: { ids: [1, 2, 3] },
  paramsSerializer: (p) => qs.stringify(p, { arrayFormat: 'brackets' })
});

Request Body

Fetch-Go auto-detects the body type and sets Content-Type:

Data typeContent-Type
Object / Arrayapplication/json (auto)
StringPassed as-is
FormDatamultipart/form-data (auto)
URLSearchParamsapplication/x-www-form-urlencoded
Blob / ArrayBuffer / StreamPassed as-is

Authentication

// HTTP Basic Auth
await fetchgo.get('/protected', {
  auth: { username: 'jane', password: 's3cret' }
});
// → Authorization: Basic amFuZTpzM2NyZXQ=

Response Schema

{
  data: {},          // response body (parsed)
  status: 200,       // HTTP status code
  statusText: 'OK',  // HTTP status message
  headers: Headers,  // Response headers (Headers object)
  config: {},        // The config used for the request
  request: Response  // The raw Response object
}

Response Type

ValueReturns
'json'Parsed JSON object (default)
'text'String
'blob'Blob
'arraybuffer'ArrayBuffer
'formdata'FormData
'stream'ReadableStream
'document' NEWDOM Document (browser only, via DOMParser)

Transforms

const api = fetchgo.create({
  transformRequest: [(data, headers) => {
    // Modify data before sending
    return JSON.stringify(data);
  }],
  transformResponse: [(data) => {
    // Modify data after receiving
    return data;
  }]
});

Transitional Options NEW

OptionDefaultDescription
silentJSONParsingtrueDon't throw on JSON parse failure, return null
forcedJSONParsingfalseForce JSON parse regardless of content-type
clarifyTimeoutErrortrueUse ETIMEDOUT instead of ECONNABORTED
const api = fetchgo.create({
  transitional: {
    silentJSONParsing: true,
    forcedJSONParsing: false,
    clarifyTimeoutError: true,
  }
});

Interceptors ENHANCED

Basic Usage

// Request interceptor
api.interceptors.request.use(
  (config) => { config.headers['X-Token'] = getToken(); return config; },
  (error) => Promise.reject(error)
);

// Response interceptor
api.interceptors.response.use(
  (response) => response,
  (error) => { if (error.status === 401) logout(); throw error; }
);

// Eject
const id = api.interceptors.request.use(myInterceptor);
api.interceptors.request.eject(id);

// Clear all
api.interceptors.request.clear();

Conditional Interceptors (runWhen) NEW

api.interceptors.request.use(
  (config) => { config.headers['X-Auth'] = getToken(); return config; },
  undefined,
  { runWhen: (config) => config.url?.startsWith('/api/') }
);

The interceptor only runs when runWhen returns true.

Synchronous Interceptors NEW

api.interceptors.request.use(
  (config) => { config.headers['X-Time'] = Date.now().toString(); return config; },
  undefined,
  { synchronous: true }
);

When all request interceptors are synchronous: true, they run without await — saving one micro-task per interceptor.

Retry

// Simple
await fetchgo.get('/api', { retry: 3 });
await fetchgo.get('/api', { retry: true }); // 3 retries

// Advanced
await fetchgo.get('/api', {
  retry: {
    retries: 5,
    delay: 500,            // initial delay ms
    backoff: 2,           // exponential: 500, 1000, 2000...
    retryOn: ['GET', 'PUT'],
    retryStatusCodes: [429, 500, 502, 503],
    retryCondition: (error) => !fetchgo.isCancel(error),
  }
});

Timeout & Cancellation

// Timeout
await fetchgo.get('/slow', { timeout: 5000 });

// Cancel with AbortController
const controller = new AbortController();
fetchgo.get('/long', { signal: controller.signal });
controller.abort();

// Both combined — whichever fires first
await fetchgo.get('/endpoint', {
  timeout: 5000,
  signal: controller.signal
});

Progress Events

// Upload
await fetchgo.post('/upload', file, {
  onUploadProgress: (e) => {
    console.log(`Upload: ${Math.round((e.progress||0)*100)}%`);
    console.log(`Rate: ${e.rate} bytes/sec`);
    console.log(`ETA: ${e.estimated}s`);
  }
});

// Download
await fetchgo.get('/large-file', {
  onDownloadProgress: (e) => {
    console.log(e.loaded, e.total, e.progress, e.bytes, e.rate, e.estimated);
  }
});

Progress Event Schema

PropertyTypeDescription
loadednumberBytes transferred so far
totalnumber?Total bytes (if known)
progressnumber?0–1 fraction complete
bytesnumberBytes in this chunk
ratenumber?Bytes per second
estimatednumber?Estimated seconds remaining
uploadboolean?true if upload event
downloadboolean?true if download event

Rate Limiting (maxRate) NEW

// Limit both directions to 100 KB/s
await fetchgo.get('/file', { maxRate: 100 * 1024 });

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

Form Handling

Shorthand Methods

await fetchgo.postForm('/upload', { name: 'John', avatar: file });
await fetchgo.putForm('/update', { name: 'Jane' });
await fetchgo.patchForm('/patch', { avatar: newFile });

URL-Encoded

await fetchgo.post('/login', { user: 'john', pass: 'secret' }, {
  formSerializer: 'urlencoded'
});

Advanced FormData Options NEW

import { toFormData, formToJSON } from 'fetch-go';

// Dot notation
toFormData({ user: { name: 'John' } }, undefined, { dots: true });
// → user.name=John (instead of user[name])

// Array indexes
toFormData({ tags: ['a','b'] }, undefined, { indexes: true });  // tags[0]=a
toFormData({ tags: ['a','b'] }, undefined, { indexes: false }); // tags[]=a
toFormData({ tags: ['a','b'] }, undefined, { indexes: null });  // tags=a

// Meta tokens
toFormData({ users: [{ name: 'a' }] }, undefined, { metaTokens: true });

// Convert back
const obj = formToJSON(formData);
OptionTypeDescription
dotsbooleanUse parent.child instead of parent[child]
indexesbool|nulltrue: arr[0], false: arr[], null: bare key
metaTokensbooleanAdd {}/[] type hints to keys

XSRF Protection

const api = fetchgo.create({
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
});
// Token is auto-read from cookies and sent as a header

Environment Auto-Detection NEW

Fetch-Go automatically selects the best adapter:

EnvironmentAdapterFeatures
Browserfetch()Full browser API support
Node.jshttp/httpsProxy, HTTP/2, agents, socket

Override with adapter: 'fetch' or adapter: 'http'.

HTTP/2 (Node.js)

await fetchgo.get('https://api.example.com', {
  httpVersion: 2,
  auth: { username: 'user', password: 'pass' },
  onDownloadProgress: (e) => console.log(e.progress)
});
💡 HTTP/2 multiplexes streams over a single TCP connection for better performance.

Proxy (Node.js)

await fetchgo.get('https://api.example.com', {
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: { username: 'proxyuser', password: 'proxypass' }
  }
});

Agents & Socket Path

import http from 'node:http';
import https from 'node:https';

await fetchgo.get('https://api.example.com', {
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ rejectUnauthorized: false }),
});

// Unix socket (e.g., Docker)
await fetchgo.get('/containers/json', {
  socketPath: '/var/run/docker.sock',
  baseURL: 'http://localhost'
});

Static Helpers NEW

// Parallel requests
const [users, posts] = await fetchgo.all([
  fetchgo.get('/users'),
  fetchgo.get('/posts'),
]);

// Spread helper
fetchgo.all([fetchgo.get('/a'), fetchgo.get('/b')])
  .then(fetchgo.spread((a, b) => console.log(a.data, b.data)));

// Cancel detection
fetchgo.isCancel(error); // true if AbortError

Error Handling

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

try {
  await api.get('/fail');
} catch (error) {
  if (isCancel(error)) {
    console.log('Cancelled');
  } else if (isFetchGoError(error)) {
    console.log(error.code);            // ERR_BAD_REQUEST
    console.log(error.status);          // 404
    console.log(error.response?.data);   // response body
    console.log(error.config);           // request config
    console.log(error.toJSON());         // serializable
  }
}

Error Codes

CodeDescription
ERR_NETWORKNetwork error (no response)
ECONNABORTEDTimeout exceeded
ETIMEDOUTTimeout (with clarifyTimeoutError: true)
ERR_CANCELEDRequest was cancelled
ERR_BAD_REQUEST4xx status code
ERR_BAD_RESPONSE5xx status code

Custom Adapters

const mockAdapter = async (config) => ({
  data: { mock: true },
  status: 200,
  statusText: 'OK',
  headers: new Headers(),
  config,
  request: new Response(),
});

await fetchgo.get('/test', { adapter: mockAdapter });

Env Config NEW

Inject custom FormData or Blob implementations:

import FormData from 'form-data';

await fetchgo.post('/upload', { file: stream }, {
  env: { FormData },
  formSerializer: 'formdata',
});

TypeScript

import fetchgo, {
  type FetchGoRequestConfig,
  type FetchGoResponse,
  type FetchGoProgressEvent,
  type InterceptorOptions,
  type FormSerializerOptions,
  type TransitionalOptions,
  type MethodHeaders,
  type EnvConfig,
} from 'fetch-go';

// Full generics
interface User { id: number; name: string; }
const { data } = await fetchgo.get<User[]>('/users');
// data is User[]

MIT © 2025 — Fetch-Go v1.2.1