HTTP Adapter
The HTTP adapter is Rezo’s default and most feature-complete adapter. It wraps Node.js’s native http and https modules with enterprise-grade capabilities including connection pooling, socket telemetry, TLS fingerprint emulation (via the stealth module), and a 50-attempt absolute safety guard against runaway redirect-and-retry chains.
import rezo from 'rezo/adapters/http'; Import and Setup
The adapter entry point exports a pre-configured instance and all core classes:
import rezo, {
Rezo,
RezoError,
RezoHeaders,
RezoFormData,
RezoCookieJar,
RezoStealth
} from 'rezo/adapters/http';
// Use the default instance
const { data } = await rezo.get('https://api.example.com/users');
// Create a custom instance with options
const client = rezo.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: { 'Accept': 'application/json' }
});
const { data: users } = await client.get('/users'); Features
Cookie Jar Integration
The HTTP adapter provides full cookie jar support with automatic persistence. Cookies from Set-Cookie response headers are stored in the jar and sent on subsequent requests that match the cookie’s domain and path:
const jar = new RezoCookieJar();
const client = rezo.create({ jar });
// Login -- cookies are automatically stored
await client.post('https://example.com/login', {
username: 'admin',
password: 'secret'
});
// Subsequent requests include session cookies automatically
const { data } = await client.get('https://example.com/dashboard');
// Inspect stored cookies
const cookies = jar.getCookiesSync('https://example.com');
console.log(cookies.map(c => `${c.key}=${c.value}`));
// Serialize for persistence across sessions
const serialized = jar.toJSON(); Proxy Support
Full proxy support for HTTP, HTTPS, and SOCKS4/5 protocols:
// HTTP proxy
const { data } = await rezo.get('https://example.com', {
proxy: {
protocol: 'http',
host: '192.168.1.100',
port: 8080
}
});
// SOCKS5 proxy with authentication
const { data } = await rezo.get('https://example.com', {
proxy: {
protocol: 'socks5',
host: '192.168.1.100',
port: 1080,
auth: {
username: 'user',
password: 'pass'
}
}
});
// Proxy as URL string
const { data } = await rezo.get('https://example.com', {
proxy: 'http://user:pass@proxy.example.com:8080'
}); Streaming
Stream responses for processing large payloads without buffering the entire body in memory. rezo.stream() returns a RezoStreamResponse synchronously and emits 'data' events as chunks arrive:
const stream = rezo.stream('https://example.com/large-file');
stream.on('data', (chunk) => {
process.stdout.write(chunk);
});
stream.on('finish', (info) => {
console.log('Stream complete. Bytes:', info.contentLength);
}); Pipe directly to a writable for zero-copy forwarding:
import { createWriteStream } from 'node:fs';
rezo.stream('https://example.com/large-file').pipe(createWriteStream('./out.bin')); File Downloads
rezo.download(url, saveTo) returns a RezoDownloadResponse event emitter immediately — it does not resolve to a RezoResponse. Listen for the finish event for the final state:
const download = rezo.download(
'https://example.com/archive.zip',
'./downloads/archive.zip'
);
download.on('progress', ({ percentage, loaded, total }) => {
console.log(`${percentage.toFixed(1)}% (${loaded}/${total} bytes)`);
});
download.on('finish', (info) => {
console.log('Saved to:', info.fileName); // './downloads/archive.zip'
console.log('File size:', info.fileSize); // 52428800
console.log('Avg speed:', info.averageSpeed, 'bytes/s');
});
download.on('error', (err) => {
console.error('Download failed:', err.code, err.message);
}); If you only need the final state, you can wrap it in a Promise:
const info = await new Promise((resolve, reject) => {
const dl = rezo.download(url, saveTo);
dl.once('finish', resolve);
dl.once('error', reject);
}); File Uploads
Upload files with progress tracking:
// Read the file into a Blob with the right Content-Type, then attach with a filename
const bytes = await fs.promises.readFile('./report.pdf');
const form = new RezoFormData();
form.append('file', new Blob([bytes], { type: 'application/pdf' }), 'report.pdf');
const upload = rezo.upload('https://example.com/upload', form);
upload.on('progress', ({ percentage }) => {
console.log(`${percentage.toFixed(1)}% uploaded`);
});
upload.on('finish', (info) => {
console.log('Server status:', info.response.status);
}); Compression
Automatic decompression for gzip, deflate, brotli, and zstd:
const { data } = await rezo.get('https://example.com/api/data', {
headers: {
'Accept-Encoding': 'gzip, deflate, br, zstd'
}
});
// Response is automatically decompressed regardless of encoding The adapter sends Accept-Encoding headers by default and transparently decompresses the response body. Compression ratios are tracked in the response’s transfer metadata.
TLS Configuration
Full control over TLS settings, including certificate pinning and Chrome 131+ fingerprint emulation via the stealth module:
// Custom TLS options
const { data } = await rezo.get('https://internal-api.example.com', {
rejectUnauthorized: false, // skip certificate verification (development only)
ca: fs.readFileSync('./ca-cert.pem'),
cert: fs.readFileSync('./client-cert.pem'),
key: fs.readFileSync('./client-key.pem')
});
// TLS fingerprinting with stealth
const stealth = new RezoStealth({ profile: 'chrome-131' });
const client = rezo.create({ stealth });
const { data } = await client.get('https://bot-protected-site.com'); Agent Pool with Keep-Alive
The adapter uses a global agent pool that maintains persistent connections with keep-alive. Agents are reused across requests to the same origin, reducing connection overhead:
// Keep-alive is enabled by default
// The agent pool automatically manages HTTP and HTTPS agents
const client = rezo.create({
timeout: 30000
});
// Multiple requests to the same origin reuse the underlying TCP connection
await client.get('https://api.example.com/users');
await client.get('https://api.example.com/posts');
await client.get('https://api.example.com/comments'); Socket Telemetry
Track low-level socket events for debugging and monitoring:
import { getSocketTelemetry } from 'rezo/adapters/http';
const telemetry = getSocketTelemetry();
// Provides socket-level timing data:
// - DNS lookup duration
// - TCP connection time
// - TLS handshake time
// - Time to first byte Per-Phase Visibility
timeout is a single millisecond value covering the whole request. The adapter does not split it into per-phase budgets — but the onTimeout hook tells you which phase tripped ('lookup' | 'connect' | 'request' | 'response' | 'socket') when the timeout fires. Combine that with a custom dnsLookup or httpsAgent if you need to enforce phase-specific budgets:
const { data } = await rezo.get('https://slow-api.example.com/data', {
timeout: 30_000,
hooks: {
onTimeout: [
(event) => {
// event.type ∈ 'connect' | 'request' | 'response' | 'socket' | 'lookup'
metrics.increment('http.timeout', { phase: event.type });
}
]
}
}); Redirect Handling
Automatic redirect following for status codes 301, 302, 303, 307, and 308, with cycle detection:
const response = await rezo.get('https://example.com/old-page', {
maxRedirects: 10, // default: 10
followRedirects: true // default: true
});
// Access the final URL after redirects
console.log(response.finalUrl); // https://example.com/new-page The adapter correctly handles method changes on 301/302/303 redirects (POST becomes GET) and preserves the method on 307/308 redirects.
Retry with Backoff
Automatic retries for transient errors with exponential backoff:
const { data } = await rezo.get('https://flaky-api.example.com/data', {
retry: {
limit: 3,
delay: 1000, // base delay in ms
maxDelay: 30000, // cap on delay
backoff: 'exponential', // exponential backoff
statusCodes: [429, 500, 502, 503, 504]
}
}); Rate Limit Wait
When the server returns a 429 (or any configured status) with a Retry-After header, the adapter can automatically wait and retry. This is configured per request via waitOnStatus:
// Wait on the default 429 using Retry-After
const { data } = await rezo.get('https://rate-limited-api.com/data', {
waitOnStatus: true
});
// Wait on a custom set of status codes
const { data } = await rezo.get('https://rate-limited-api.com/data', {
waitOnStatus: [429, 503],
maxWaitTime: 60_000, // give up if Retry-After exceeds 60 s
maxWaitAttempts: 3, // at most 3 waits before falling through to retry
defaultWaitTime: 1_000 // fallback when no Retry-After header is sent
}); See Request Configuration for waitTimeSource, which lets you read the wait time from a custom header, JSON body path, or function.
Response Caching
Cache responses in memory to avoid redundant network requests. The cache option takes either true (defaults) or { response, dns }:
const client = rezo.create({
cache: {
response: {
enable: true,
ttl: 60_000, // 1 minute time-to-live
maxEntries: 500 // LRU capacity
}
}
});
// First request hits the network
const { data: first } = await client.get('https://api.example.com/config');
// Second request returns cached response immediately
const { data: second } = await client.get('https://api.example.com/config'); DNS Caching
The adapter integrates with Rezo’s DNS cache to avoid repeated DNS lookups:
const client = rezo.create({
dnsCache: true // enable DNS caching
});
// DNS results are cached and reused across requests
await client.get('https://api.example.com/endpoint1');
await client.get('https://api.example.com/endpoint2'); // reuses DNS result 50-Attempt Safety Guard
The adapter enforces an absolute maximum of 50 total attempts (combining redirects and retries) per request to prevent infinite loops, regardless of your maxRedirects and retry.limit configuration:
// Even if maxRedirects is 30 and retry.limit is 30,
// the adapter will throw after 50 total attempts
const { data } = await rezo.get('https://example.com', {
maxRedirects: 30,
retry: { limit: 30 }
});
// Throws: "Absolute maximum attempts (50) exceeded.
// This prevents infinite loops from retries and redirects." Debug Mode
Enable detailed logging of every request phase:
const { data } = await rezo.get('https://api.example.com/users', {
debug: true
});
// [Rezo Debug] GET https://api.example.com/users
// [Rezo Debug] Request ID: req_abc123
// [Rezo Debug] Request Headers: { ... }
// [Rezo Debug] Response: 200 OK (142.35ms)
// Or track just URLs and redirects
const { data } = await rezo.get('https://example.com/old', {
trackUrl: true
});
// [Rezo Track] -> GET https://example.com/old
// [Rezo Track] -> 301 -> https://example.com/new
// [Rezo Track] 200 OK When to Use This Adapter
The HTTP adapter is the right choice when:
- You are running on Node.js or Bun
- You need cookie jar, proxy, or TLS configuration support
- You want connection pooling and keep-alive for performance
- You need upload or download progress tracking
- You want streaming responses with backpressure handling
- You need the fullest feature set available in Rezo
For HTTP/2 multiplexing, use the HTTP/2 adapter instead. For browser environments, use the Fetch adapter.