Switch to Rezo

From superagent

superagent pioneered the chainable HTTP API for JavaScript and has been a reliable workhorse for over a decade. If your project still depends on it, switching to Rezo gives you modern TypeScript, HTTP/2, built-in cookies, proxy rotation, stealth, lifecycle hooks, streaming with progress, and support for every JavaScript runtime — while the core API patterns remain straightforward.

Basic GET

// superagent
import superagent from 'superagent';

const { body } = await superagent.get('https://api.example.com/users');

// Rezo
import rezo from 'rezo';

const { data } = await rezo.get('https://api.example.com/users');

POST with JSON

// superagent
const { body } = await superagent
  .post('https://api.example.com/users')
  .send({ name: 'Ada Lovelace', email: 'ada@example.com' })
  .set('Content-Type', 'application/json');

// Rezo
const { data } = await rezo.postJson('https://api.example.com/users', {
  name: 'Ada Lovelace',
  email: 'ada@example.com',
});

Headers

// superagent -- chainable .set()
const { body } = await superagent
  .get('https://api.example.com/data')
  .set('Authorization', 'Bearer token123')
  .set('Accept', 'application/json');

// Rezo -- headers object
const { data } = await rezo.get('https://api.example.com/data', {
  headers: {
    'Authorization': 'Bearer token123',
    'Accept': 'application/json',
  },
});

Authentication

// superagent
const { body } = await superagent
  .get('https://api.example.com/data')
  .auth('user', 'pass');

// bearer token
const { body: body2 } = await superagent
  .get('https://api.example.com/data')
  .auth('my-token', { type: 'bearer' });

// Rezo
const { data } = await rezo.get('https://api.example.com/data', {
  auth: { username: 'user', password: 'pass' },
});

// bearer token
const { data: data2 } = await rezo.get('https://api.example.com/data', {
  headers: { 'Authorization': 'Bearer my-token' },
});

Query Parameters

// superagent
const { body } = await superagent
  .get('https://api.example.com/users')
  .query({ page: 2, limit: 25 });

// Rezo
const { data } = await rezo.get('https://api.example.com/users', {
  params: { page: 2, limit: 25 },
});

File Upload

// superagent
const { body } = await superagent
  .post('https://api.example.com/upload')
  .attach('file', './report.pdf')
  .field('description', 'Monthly report');

// Rezo
import { RezoFormData } from 'rezo';
import { promises as fs } from 'node:fs';

// RezoFormData accepts string | Blob | Buffer (not Node Readable streams).
// Read the file in, then attach as a Blob with the right MIME type.
const bytes = await fs.readFile('./report.pdf');
const form = new RezoFormData();
form.append('file', new Blob([bytes], { type: 'application/pdf' }), 'report.pdf');
form.append('description', 'Monthly report');

const { data } = await rezo.post('https://api.example.com/upload', form);

Rezo also provides a dedicated upload method with progress tracking:

const upload = rezo.upload('https://api.example.com/upload', form);
upload.on('progress', ({ percentage }) => console.log(`${percentage}%`));
upload.on('finish', (info) => console.log('Server status:', info.response.status));

Error Handling

// superagent
try {
  await superagent.get('https://api.example.com/missing');
} catch (error) {
  console.log(error.status);    // 404
  console.log(error.response);  // response object
  console.log(error.message);   // "Not Found"
}

// Rezo -- structured errors with codes and recovery
import rezo, { RezoError } from 'rezo';

try {
  await rezo.get('https://api.example.com/missing');
} catch (error) {
  if (rezo.isRezoError(error)) {
    console.log(error.code);          // "REZ_HTTP_ERROR"
    console.log(error.status);        // 404
    console.log(error.isHttpError);   // true (any non-2xx response)
    console.log(error.isTimeout);     // false
    console.log(error.suggestion);    // "Check the status code and response body for more details..."
  }
}

Timeouts

// superagent
await superagent
  .get('https://api.example.com/slow')
  .timeout({ response: 5000, deadline: 30000 });

// Rezo -- single number, or staged budgets per phase
const { data } = await rezo.get('https://api.example.com/slow', {
  timeout: 30_000
});

// Or per-phase budgets via StagedTimeoutConfig:
const { data: staged } = await rezo.get('https://api.example.com/slow', {
  timeout: {
    connect: 3_000,
    headers: 5_000,
    body: 15_000,
    total: 30_000,
  }
});

Retry

// superagent
await superagent
  .get('https://api.example.com/flaky')
  .retry(3);

// Rezo -- full retry configuration
const { data } = await rezo.get('https://api.example.com/flaky', {
  retry: {
    limit: 3,
    delay: 1000,
    maxDelay: 10_000,
    backoff: 'exponential',
    statusCodes: [429, 500, 502, 503, 504],
  }
});

Redirects

// superagent
await superagent
  .get('https://example.com/old-page')
  .redirects(5);

// Rezo
const { data } = await rezo.get('https://example.com/old-page', {
  maxRedirects: 5,
});

What You Gain by Switching

TypeScript-First

superagent’s types are community-maintained @types/superagent. Rezo is written in strict TypeScript with generics, overloads, and discriminated unions.

interface User {
  id: number;
  name: string;
}

const { data } = await rezo.get<User[]>('/api/users');
// data is User[] with full IDE autocomplete

HTTP/2

superagent does not support HTTP/2. Rezo provides a dedicated HTTP/2 adapter:

// The adapter entry exports a default rezo instance pre-wired with HTTP/2
import rezo from 'rezo/adapters/http2';

const client = rezo.create({ baseURL: 'https://api.example.com' });

superagent requires the superagent-cookie plugin. Rezo includes a cookie jar with persistence:

import rezo, { CookieJar } from 'rezo';

const jar = new CookieJar();
const client = rezo.create({ jar });

await client.postJson('https://example.com/login', { user: 'ada', pass: 'secret' });
const { data } = await client.get('https://example.com/dashboard');

jar.toJSON();             // export for persistence
jar.toNetscapeCookie();   // cURL-compatible format

Proxy Rotation

import rezo, { ProxyManager } from 'rezo';

const client = rezo.create({
  proxyManager: new ProxyManager({
    rotation: 'random',
    proxies: [
      'http://proxy1:8080',
      'socks5://proxy2:1080',
      'https://proxy3:3128',
    ],
    autoDisableDeadProxies: true,
  }),
});
// Automatic rotation, health monitoring, failover

Stealth Mode

import rezo, { RezoStealth } from 'rezo';

const client = rezo.create({
  stealth: new RezoStealth({ family: 'chrome', rotate: true }),
});

18 browser profiles with TLS fingerprinting, HTTP/2 SETTINGS emulation, and header ordering.

26 Lifecycle Hooks

const client = rezo.create({
  hooks: {
    beforeRequest: [(config) => {
      config.headers.set('X-Request-ID', crypto.randomUUID());
    }],
    afterResponse: [(response, config) => {
      console.log(`${response.status} in ${config.timing.responseEnd - config.timing.startTime}ms`);
      return response;
    }],
    beforeRetry: [(config, error, retryCount) => {
      console.log(`Retry #${retryCount}`);
    }],
  }
});

Streaming and Downloads

// Stream response — rezo.stream() returns a RezoStreamResponse emitter directly
const stream = rezo.stream('https://example.com/feed');
stream.on('data', (chunk) => process.stdout.write(chunk));
stream.on('finish', () => console.log('Done'));

// Download with progress events
const dl = rezo.download('https://example.com/large-file.zip', './file.zip');
dl.on('progress', ({ percentage, loaded, total }) => {
  console.log(`${percentage}% (${loaded}/${total})`);
});
dl.on('finish', (info) => console.log('Saved to', info.fileName));

Multi-Runtime Support

superagent works in Node.js and browsers. Rezo adds Deno, Bun, React Native, and edge runtimes with automatic adapter selection.

Web Crawler and Site Cloning

import { Crawler } from 'rezo/crawler';

const crawler = new Crawler({
  baseUrl: 'https://example.com',
  maxDepth: 3,
  concurrency: 10,
});

crawler.onDocument(async (document) => {
  console.log(document.location?.href);
});

await crawler.visit('https://example.com');
await crawler.done();
import { Wget } from 'rezo/wget';

await new Wget({
  recursive: { enabled: true, convertLinks: true, pageRequisites: true },
  download: { outputDir: './mirror' }
}).get('https://docs.example.com');

Feature Comparison

FeatureRezosuperagent
Node.jsYesYes
BrowsersYesYes
Deno / EdgeYesNo
React NativeYesPartial
TypeScriptFirst-class strict typesCommunity types
HTTP/2YesNo
Cookie jarBuilt-inPlugin
Cookie persistenceJSON and NetscapeNo
Proxy supportBuilt-in (HTTP, HTTPS, SOCKS4, SOCKS5)Plugin
Proxy rotationBuilt-in with health monitoringNo
Stealth mode18 browser profilesNo
Lifecycle hooks26 hooksNo
InterceptorsRequest and responseNo
Retry with backoffFull (backoff, conditions, status codes)Basic (count only)
Staged timeoutsFull (connect, headers, body, total)Partial (response, deadline)
Downloads with progressBuilt-inNo
Uploads with progressBuilt-inNo
Request queueBuilt-in with rate limitingNo
Response cachingBuilt-inNo
DNS cachingBuilt-inNo
Web crawlerBuilt-inNo
Site cloningBuilt-inNo
cURL adapterYesNo
Error codes70+ with recovery suggestionsNone
Active maintenanceActiveMinimal