From Got
Got is a well-designed HTTP library for Node.js with hooks, retry, and pagination. If you have been productive with Got, you will feel at home with Rezo — the concepts are similar. The difference is scope: Rezo runs on every JavaScript runtime, and ships with features Got does not offer: stealth mode, proxy rotation, a web crawler, site cloning, and a cURL adapter.
Basic Requests
// Got
import got from 'got';
const { body } = await got('https://api.example.com/users').json();
// Rezo -- parsed JSON in response.data
import rezo from 'rezo';
const { data } = await rezo.get('https://api.example.com/users'); Instance Creation
// Got
const client = got.extend({
prefixUrl: 'https://api.example.com',
timeout: { request: 5000 },
headers: { 'X-API-Key': 'abc123' },
});
const { body } = await client.get('users').json();
// Rezo
import rezo from 'rezo';
const client = rezo.create({
baseURL: 'https://api.example.com',
timeout: { total: 5000 },
headers: { 'X-API-Key': 'abc123' },
});
const { data } = await client.get('/users'); POST with JSON
// Got
const { body } = await got.post('https://api.example.com/users', {
json: { name: 'Ada Lovelace' },
}).json();
// Rezo
const { data } = await rezo.postJson('https://api.example.com/users', {
name: 'Ada Lovelace',
}); Hooks
Got provides 9 hook points. Rezo provides 26, covering every phase from DNS resolution to cookie processing.
// Got
const client = got.extend({
hooks: {
beforeRequest: [(options) => { /* ... */ }],
afterResponse: [(response) => { return response; }],
beforeRetry: [(error, retryCount) => { /* ... */ }],
beforeRedirect: [(options, response) => { /* ... */ }],
beforeError: [(error) => { return error; }],
init: [(plain, options) => { /* ... */ }],
}
});
// Rezo -- same concept, more hook points
const client = rezo.create({
hooks: {
// Same as Got
beforeRequest: [(config, context) => { /* ... */ }],
afterResponse: [(response, config, context) => { return response; }],
beforeRetry: [(config, error, context) => { /* ... */ }],
beforeRedirect: [(config, response) => { /* ... */ }],
beforeError: [(error) => { return error; }],
init: [(config) => { /* ... */ }],
// Rezo-only hooks
afterHeaders: [(headers, config) => { /* ... */ }],
afterCookie: [(cookies, config) => { /* ... */ }],
beforeDnsLookup: [(hostname) => { /* ... */ }],
afterDnsLookup: [(address, hostname) => { /* ... */ }],
beforeTcpConnect: [(options) => { /* ... */ }],
afterTcpConnect: [(socket) => { /* ... */ }],
beforeTlsHandshake: [(options) => { /* ... */ }],
afterTlsHandshake: [(socket) => { /* ... */ }],
// ... and more
}
}); Retry
Both libraries support retry with backoff. Rezo adds custom status codes and condition functions.
// Got
const client = got.extend({
retry: {
limit: 3,
methods: ['GET', 'POST'],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
calculateDelay: ({ attemptCount }) => attemptCount * 1000,
}
});
// Rezo
const client = rezo.create({
retry: {
limit: 3,
methods: ['GET', 'POST'],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
backoff: { delay: 1000, maxDelay: 10_000 },
condition: (error, attempt) => {
// Custom logic beyond status codes
return error.code !== 'REZ_AUTH_ERROR';
},
}
}); Pagination
Both libraries support pagination. Rezo uses an async generator pattern.
// Got
const results = await got.paginate.all('https://api.example.com/items', {
pagination: {
transform: (response) => JSON.parse(response.body),
paginate: ({ response, currentItems }) => {
const next = response.headers.link?.match(/<(.+?)>; rel="next"/)?.[1];
if (!next) return false;
return { url: next };
},
}
});
// Rezo -- async generator
const client = rezo.create();
const pages = client.paginate('https://api.example.com/items', {
paginate: {
getNextUrl: (response) => {
const link = response.headers.get('link');
return link?.match(/<(.+?)>; rel="next"/)?.[1] || null;
},
getData: (response) => response.data,
}
});
const allItems = [];
for await (const page of pages) {
allItems.push(...page);
} Timeouts
// Got -- partial staged timeouts
const client = got.extend({
timeout: {
lookup: 1000,
connect: 3000,
secureConnect: 3000,
socket: 5000,
send: 10000,
response: 10000,
request: 30000,
}
});
// Rezo -- staged timeouts
const client = rezo.create({
timeout: {
connect: 5_000,
headers: 10_000,
body: 30_000,
total: 60_000,
}
}); Error Handling
// Got
import got, { HTTPError, TimeoutError } from 'got';
try {
await got('https://api.example.com/data');
} catch (error) {
if (error instanceof HTTPError) {
console.log(error.response.statusCode);
} else if (error instanceof TimeoutError) {
console.log('Request timed out');
}
}
// Rezo -- single error class with boolean flags
import rezo, { RezoError } from 'rezo';
try {
await rezo.get('https://api.example.com/data');
} catch (error) {
if (rezo.isRezoError(error)) {
console.log(error.code); // "REZ_HTTP_ERROR" or "REZ_TIMEOUT"
console.log(error.status); // 404
console.log(error.isTimeout); // false
console.log(error.isClientError); // true
console.log(error.suggestion); // recovery advice
}
} Downloads
// Got
import { pipeline } from 'node:stream/promises';
import { createWriteStream } from 'node:fs';
const downloadStream = got.stream('https://example.com/file.zip');
downloadStream.on('downloadProgress', ({ transferred, total, percent }) => {
console.log(`${(percent * 100).toFixed(1)}%`);
});
await pipeline(downloadStream, createWriteStream('./file.zip'));
// Rezo
await rezo.download('https://example.com/file.zip', {
outputPath: './file.zip',
onProgress: ({ percent, transferred, total }) => {
console.log(`${percent}%`);
}
}); What You Gain by Switching
Multi-Runtime Support
Got is Node.js-only. Rezo runs on Node.js, Bun, Deno, browsers, React Native, and edge runtimes with the same API.
Stealth Mode
import rezo from 'rezo';
import { RezoStealth } from 'rezo/stealth';
const client = rezo.create({
stealth: new RezoStealth({ family: 'chrome', rotate: true }),
}); 18 browser profiles with TLS fingerprinting, HTTP/2 SETTINGS emulation, header ordering, and client hints.
Proxy Rotation
import { Rezo, ProxyManager } from 'rezo';
const client = rezo.create({
proxyManager: new ProxyManager([
'http://proxy1:8080',
'socks5://proxy2:1080',
'https://proxy3:3128',
]),
}); Automatic rotation with health monitoring. Got requires manual agent setup for each proxy type.
Built-In Cookie Jar
import { Rezo, CookieJar } from 'rezo';
const jar = new CookieJar();
const client = rezo.create({ jar });
// Cookies managed automatically -- export to JSON or Netscape
jar.toJSON();
jar.toNetscape(); Got requires tough-cookie as a separate dependency.
Web Crawler
import { Crawler } from 'rezo/crawler';
const crawler = new Crawler({
startUrls: ['https://example.com'],
maxDepth: 3,
concurrency: 10,
});
for await (const result of crawler) {
console.log(result.url, result.status);
} Queue-based crawling with robots.txt compliance, SQLite persistence, and memory monitoring. Nothing comparable in Got.
Site Cloning
import { Wget } from 'rezo/wget';
await new Wget({
url: 'https://docs.example.com',
outputDir: './mirror',
depth: 5,
convertLinks: true,
}).run(); cURL Adapter
import rezo from 'rezo';
import curlAdapter from 'rezo/adapters/curl';
const client = rezo.create({ adapter: curlAdapter }); Useful for TLS fingerprint impersonation and debugging. Not available in Got.
Feature Comparison
| Feature | Rezo | Got |
|---|---|---|
| Node.js | Yes | Yes |
| Browsers | Yes | No |
| Deno / Edge / React Native | Yes | No |
| Hooks | 26 | 9 |
| Retry with backoff | Yes | Yes |
| Pagination | Yes (async generator) | Yes |
| Timeouts | Staged (4 phases) | Staged (6 phases) |
| Cookie jar | Built-in | Plugin (tough-cookie) |
| Cookie persistence | JSON and Netscape | No |
| Proxy rotation | Built-in | No |
| SOCKS proxy | Built-in | No |
| Stealth mode | 18 profiles | No |
| Web crawler | Built-in | No |
| Site cloning | Built-in | No |
| cURL adapter | Yes | No |
| HTTP/2 | Yes | Yes |
| Downloads with progress | Yes | Yes |
| Request queue | Built-in | No |
| Response caching | Built-in | Plugin |
| DNS caching | Built-in | Plugin |
| Error codes | 70+ | ~12 |
| Recovery suggestions | Yes | No |