Switch to Rezo

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.

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

FeatureRezoGot
Node.jsYesYes
BrowsersYesNo
Deno / Edge / React NativeYesNo
Hooks269
Retry with backoffYesYes
PaginationYes (async generator)Yes
TimeoutsStaged (4 phases)Staged (6 phases)
Cookie jarBuilt-inPlugin (tough-cookie)
Cookie persistenceJSON and NetscapeNo
Proxy rotationBuilt-inNo
SOCKS proxyBuilt-inNo
Stealth mode18 profilesNo
Web crawlerBuilt-inNo
Site cloningBuilt-inNo
cURL adapterYesNo
HTTP/2YesYes
Downloads with progressYesYes
Request queueBuilt-inNo
Response cachingBuilt-inPlugin
DNS cachingBuilt-inPlugin
Error codes70+~12
Recovery suggestionsYesNo