From Axios
If you know Axios, you already know Rezo. The API patterns are nearly identical — method signatures, interceptors, config merging, and error handling all work the same way. The difference is what ships out of the box: cookies, proxies, stealth, hooks, streaming, HTTP/2, and support for every JavaScript runtime.
Import Changes
// Axios
import axios from 'axios';
// Rezo -- same default export pattern
import rezo from 'rezo'; Instance Creation
// Axios
const client = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
headers: { 'X-API-Key': 'abc123' },
});
// Rezo -- same pattern
const client = rezo.create({
baseURL: 'https://api.example.com',
timeout: 5000,
headers: { 'X-API-Key': 'abc123' },
}); timeout covers the entire request from connection to response, just like Axios. Rezo also supports per-status rate-limit waiting (waitOnStatus) and a richer retry config (see Retry).
HTTP Methods
Every method you use in Axios works identically in Rezo:
// These are the same in both libraries
await client.get('/users');
await client.post('/users', { name: 'Ada' });
await client.put('/users/1', { name: 'Ada Lovelace' });
await client.patch('/users/1', { email: 'ada@example.com' });
await client.delete('/users/1');
await client.head('/users');
await client.options('/users'); Rezo also adds convenience methods for common content types:
// JSON with automatic Content-Type
await client.postJson('/users', { name: 'Ada' });
await client.putJson('/users/1', { name: 'Ada Lovelace' });
await client.patchJson('/users/1', { email: 'ada@example.com' });
// URL-encoded form
await client.postForm('/login', { user: 'ada', pass: 'secret' }); Interceptors
The interceptor API is identical:
// Axios
axios.interceptors.request.use(
(config) => {
config.headers.Authorization = `Bearer ${getToken()}`;
return config;
},
(error) => Promise.reject(error)
);
axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
return refreshAndRetry(error.config);
}
return Promise.reject(error);
}
);
// Rezo -- same API, on the default instance
rezo.interceptors.request.use(
(config) => {
config.headers.set('Authorization', `Bearer ${getToken()}`);
return config;
},
(error) => Promise.reject(error)
);
rezo.interceptors.response.use(
(response) => response,
(error) => {
if (error.status === 401) {
return refreshAndRetry(error.config);
}
return Promise.reject(error);
}
); Rezo’s interceptors also support a runWhen predicate:
rezo.interceptors.request.use(
(config) => { /* ... */ return config; },
null,
{ runWhen: (config) => config.url.startsWith('/api/') }
); Error Handling
Axios errors carry response, request, and code. Rezo carries the same information plus boolean flags and recovery suggestions.
// Axios
try {
await axios.get('/api/data');
} catch (error) {
if (error.response) {
console.log(error.response.status);
console.log(error.response.data);
} else if (error.request) {
console.log('No response received');
} else {
console.log(error.message);
}
}
// Rezo
try {
await rezo.get('/api/data');
} catch (error) {
if (rezo.isRezoError(error)) {
console.log(error.code); // e.g. "REZ_HTTP_ERROR"
console.log(error.status); // 404
console.log(error.response?.data);
// Boolean flags — categorize without parsing status codes by hand
console.log(error.isHttpError); // true for any non-2xx response
console.log(error.isNetworkError); // ECONNREFUSED, ECONNRESET, ENOTFOUND, …
console.log(error.isTimeout); // ETIMEDOUT and friends
console.log(error.isAborted); // ABORT_ERR
console.log(error.isProxyError); // proxy failed
console.log(error.isSocksError); // SOCKS specifically
console.log(error.isTlsError); // cert / handshake failure
console.log(error.isRetryable); // safe to retry
// Recovery suggestion — human-readable hint per error code
console.log(error.suggestion);
}
} Query Parameters
// Axios
await axios.get('/users', {
params: { page: 2, limit: 25 },
paramsSerializer: (params) => qs.stringify(params),
});
// Rezo -- same pattern, custom serializer optional
await rezo.get('/users', {
params: { page: 2, limit: 25 },
}); Typed Responses
// Axios
const { data } = await axios.get<User[]>('/users');
// Rezo -- identical
const { data } = await rezo.get<User[]>('/users'); Cancel Requests
// Axios (modern)
const controller = new AbortController();
axios.get('/slow', { signal: controller.signal });
controller.abort();
// Rezo -- same AbortController pattern
const controller = new AbortController();
rezo.get('/slow', { signal: controller.signal });
controller.abort(); Axios Compatibility Helpers
Rezo re-exports the legacy Axios names so existing code that uses axios.Cancel / axios.all / etc. keeps working with a single import swap:
import rezo, {
Cancel, // = RezoError (Axios called its error class Cancel)
CancelToken, // = AbortController (the modern equivalent of Axios's CancelToken)
isCancel, // = (err) => err.code === 'ECONNABORTED'
isRezoError, // = RezoError.isRezoError — replaces axios.isAxiosError
all, // = Promise.all.bind(Promise)
spread, // = (fn) => (arr) => fn(...arr)
} from 'rezo';
// Old Axios pattern, ported as-is:
const [users, posts] = await all([
rezo.get('/users'),
rezo.get('/posts'),
]).then(spread((u, p) => [u.data, p.data]));
// Cancellation — use AbortController directly; CancelToken is just an alias.
const controller = new CancelToken();
rezo.get('/slow', { signal: controller.signal });
controller.abort(); If you’re writing new code, prefer Promise.all, AbortController, and RezoError.isRezoError(err) directly — the helpers exist so a search-and-replace migration from Axios doesn’t break.
What You Gain by Switching
These are features Axios does not provide or requires third-party plugins to achieve.
Built-In Cookie Jar
Axios needs axios-cookiejar-support and tough-cookie to handle cookies. Rezo has a cookie jar built into every instance — no setup needed.
// Axios -- requires two extra packages
import axios from 'axios';
import { wrapper } from 'axios-cookiejar-support';
import { CookieJar } from 'tough-cookie';
const jar = new CookieJar();
const client = wrapper(axios.create({ jar }));
// Rezo -- cookies work automatically
const { data } = await rezo.get('https://example.com/login');
// Cookies from the response are stored and sent on subsequent requests
const { data: profile } = await rezo.get('https://example.com/profile'); If you need external control over the jar (exporting, importing, inspecting), you can swap the default instance’s jar in place — or, if you want isolation from the default, build a configured instance:
import rezo, { CookieJar } from 'rezo';
const jar = new CookieJar();
// App-wide: use this jar for every request from the default instance
rezo.jar = jar;
// Or isolated: a separate client whose cookies don't bleed into `rezo`
const client = rezo.create({ jar }); Proxy Manager
const client = rezo.create({
proxyManager: {
rotation: 'random',
proxies: ['http://proxy1:8080', 'socks5://proxy2:1080'],
autoDisableDeadProxies: true
}
}); Stealth Mode
import { RezoStealth } from 'rezo';
const client = rezo.create({
stealth: RezoStealth.chrome()
}); 26 Lifecycle Hooks
Beyond interceptors, Rezo provides hooks for DNS resolution, TCP connect, TLS handshake, redirects, retries, cookies, and more. Like interceptors, hooks live on the default instance — push directly into the array:
rezo.hooks.beforeRequest.push((config) => { /* ... */ });
rezo.hooks.afterResponse.push((response, config) => { return response; });
rezo.hooks.beforeRetry.push((config, error, retryCount) => { /* ... */ });
rezo.hooks.onDns.push((event) => { /* DNS metrics */ }); Or pass them at instance construction when you want a scoped configuration:
const client = rezo.create({
hooks: {
beforeRequest: [(config) => { /* ... */ }],
afterResponse: [(response, config) => { return response; }],
}
}); Streaming and Downloads
const download = rezo.download(
'https://example.com/large-file.zip',
'./downloads/file.zip'
);
download.on('progress', (p) => console.log(`${p.percentage}%`));
download.on('finish', (info) => console.log('Saved to', info.fileName)); HTTP/2
import rezo from 'rezo/adapters/http2';
const { data } = await rezo.get('https://api.example.com'); cURL Adapter
import rezo from 'rezo/adapters/curl';
const { data } = await rezo.get('https://api.example.com'); Staged Timeouts
A single timeout: number covers the whole request, just like Axios. Pass a StagedTimeoutConfig object to budget each phase independently:
// Per request — no instance needed
await rezo.get('https://api.example.com/slow', {
timeout: {
connect: 5_000,
headers: 10_000,
body: 30_000,
total: 60_000,
}
}); Quick Migration Checklist
- Replace
import axios from 'axios'withimport rezo from 'rezo' - Replace
axios.create()withrezo.create() - Replace
error.response.statuswitherror.status - Replace header string assignments with
config.headers.set()in interceptors - Remove
axios-cookiejar-support— cookies are built in - Remove any retry plugins — use
retry: { limit: 3 }in config - Enjoy everything else that comes for free