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.
Auto-detects environment: native fetch() in browsers, http/https adapter in
Node.js. No manual config needed.
Intercept requests and responses with runWhen conditional filtering and
synchronous mode for zero-overhead processing.
Exponential backoff retry with configurable delays, status codes, and custom conditions. No plugins needed.
Automatically serializes request bodies and parses JSON responses. Zero boilerplate.
Native AbortController support. No deprecated CancelToken — just standard
APIs.
Full generic support. get<User[]>('/users') returns properly typed data.
Auto-convert to FormData or URLSearchParams with dots,
indexes, and metaTokens options.
Reads XSRF tokens from cookies and sends them as headers automatically.
Upload/download progress events with maxRate throttling for bandwidth control.
Native HTTP/2 support in Node.js via httpVersion: 2. Multiplexed streams with full
feature support.
Set default headers per method: common, get, post, etc. Just
like Axios.
silentJSONParsing, forcedJSONParsing, clarifyTimeoutError for
fine control.
Built-in auth config for HTTP Basic Auth. Auto-encodes to Authorization
header.
Node.js proxy with auth, plus custom httpAgent/httpsAgent and
socketPath.
all(), spread(), isCancel() — full Axios-compatible utility
methods.
fetch() requests from the browserhttp requests from Node.jsrunWhen)AbortControllerAbortSignalmultipart/form-data serializationx-www-form-urlencoded serializationdots, indexes, metaTokens)maxRate)env for polyfill injectionhttpVersion: 2)auth config)httpAgent / httpsAgentpostForm / putForm / patchFormall() / spread() / isCancel()getUri() / toFormData() / formToJSON()beforeRedirect hooksocketPath for Unix socketsresponseType: 'document'| 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() |
Using npm:
Using yarn:
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 } });
const { data } = await fetchgo.post('/api/users', { name: 'John', email: 'john@example.com' });
const api = fetchgo.create({ baseURL: 'https://api.example.com', headers: { common: { 'Accept': 'application/json' }, get: { 'Cache-Control': 'no-cache' }, post: { 'Content-Type': 'application/json' }, } });
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, } );
// 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] });
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
// fetchgo.all() + fetchgo.spread() const [users, posts] = await fetchgo.all([ fetchgo.get('/api/users'), fetchgo.get('/api/posts'), ]);
const api = fetchgo.create({ transitional: { silentJSONParsing: true, // Don't throw on bad JSON forcedJSONParsing: false, // Force JSON parse clarifyTimeoutError: true, // ETIMEDOUT code } });
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" } }
// 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');