Quick Start
This guide walks through the core features of Rezo: making requests, sending data, handling errors, managing cookies, and working with streams and downloads.
Making a GET Request
The default import is a pre-configured instance ready to use:
import rezo from 'rezo';
const response = await rezo.get('https://api.example.com/users');
console.log(response.status); // 200
console.log(response.statusText); // 'OK'
console.log(response.data); // parsed response body
console.log(response.headers); // RezoHeaders instance
console.log(response.finalUrl); // final URL after any redirects
console.log(response.cookies); // cookies from the response Query Parameters
Pass query parameters via the params option:
const response = await rezo.get('https://api.example.com/users', {
params: { page: 2, limit: 25, sort: 'name' }
});
// Requests: https://api.example.com/users?page=2&limit=25&sort=name Typed Responses
Use generics to type the response data:
interface User {
id: number;
name: string;
email: string;
}
const { data } = await rezo.get<User[]>('https://api.example.com/users');
// data is User[] POST with JSON
Use postJson to send a JSON payload with the correct Content-Type header set automatically:
const response = await rezo.postJson('https://api.example.com/users', {
name: 'Ada Lovelace',
email: 'ada@example.com'
});
console.log(response.status); // 201
console.log(response.data); // { id: 1, name: 'Ada Lovelace', ... } The same pattern works for putJson and patchJson:
await rezo.putJson('https://api.example.com/users/1', { name: 'Ada L.' });
await rezo.patchJson('https://api.example.com/users/1', { email: 'new@example.com' }); POST with Form Data
URL-Encoded Form
const response = await rezo.postForm('https://api.example.com/login', {
username: 'ada',
password: 'secret'
});
// Content-Type: application/x-www-form-urlencoded Multipart Form (File Uploads)
import { RezoFormData } from 'rezo';
const form = new RezoFormData();
form.append('name', 'profile-photo');
form.append('file', await Bun.file('./photo.jpg').arrayBuffer(), {
filename: 'photo.jpg',
contentType: 'image/jpeg'
});
const response = await rezo.postMultipart('https://api.example.com/upload', form); You can also pass a plain object — Rezo converts it to multipart automatically:
const response = await rezo.postMultipart('https://api.example.com/upload', {
name: 'document',
metadata: JSON.stringify({ tags: ['report', '2024'] })
}); Creating Instances with Defaults
Create a dedicated client with shared configuration. Every request inherits these defaults:
import { Rezo } from 'rezo';
const api = new Rezo({
baseURL: 'https://api.example.com/v2',
timeout: 10_000,
headers: {
Authorization: 'Bearer sk-your-token',
'Accept': 'application/json'
},
followRedirects: true,
maxRedirects: 5
});
// Requests go to https://api.example.com/v2/users
const users = await api.get('/users');
const user = await api.get('/users/42'); Using rezo.create()
The default instance also has a create method that returns a new Rezo instance:
import rezo from 'rezo';
const github = rezo.create({
baseURL: 'https://api.github.com',
headers: { Accept: 'application/vnd.github.v3+json' }
});
const { data } = await github.get('/repos/nodejs/node'); Error Handling
All request errors throw a RezoError with structured information about what went wrong:
import rezo, { RezoError } from 'rezo';
try {
await rezo.get('https://api.example.com/protected');
} catch (error) {
if (error instanceof RezoError) {
console.log(error.message); // Human-readable error message
console.log(error.code); // Error code string, e.g. 'ERR_BAD_REQUEST'
console.log(error.status); // HTTP status code (if available)
console.log(error.config); // The request configuration
console.log(error.response); // The response object (if the server responded)
console.log(error.suggestion); // Recovery suggestion
// Boolean flags for common error categories
if (error.isTimeout) {
console.log('Request timed out');
}
if (error.isNetworkError) {
console.log('Network connectivity issue');
}
if (error.isHttpError) {
console.log(`Server returned ${error.status}`);
}
if (error.isRetryable) {
console.log('This error is safe to retry');
}
}
} Static Type Guard
Use RezoError.isRezoError() when you cannot use instanceof (for example, across module boundaries):
import { RezoError } from 'rezo';
try {
await rezo.get('/endpoint');
} catch (error) {
if (RezoError.isRezoError(error)) {
// error is narrowed to RezoError
console.log(error.code);
}
} Interceptors
Register request and response interceptors to transform data or handle cross-cutting concerns:
const client = new Rezo({ baseURL: 'https://api.example.com' });
// Request interceptor -- runs before every request
client.interceptors.request.use(
(config) => {
config.headers = config.headers || {};
config.headers['X-Request-Id'] = crypto.randomUUID();
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor -- runs after every response
client.interceptors.response.use(
(response) => {
console.log(`${response.config.method} ${response.finalUrl} -> ${response.status}`);
return response;
},
(error) => {
if (error.status === 401) {
// Handle token refresh, redirect to login, etc.
}
return Promise.reject(error);
}
); Cookie Persistence
Rezo manages cookies automatically. Every instance has a built-in cookie jar that stores cookies per domain and path, respects expiration, and sends the right cookies with each request.
import { Rezo, CookieJar } from 'rezo';
// Cookies persist in memory across requests
const client = new Rezo();
await client.get('https://example.com/login'); // receives Set-Cookie
await client.get('https://example.com/dashboard'); // sends cookie automatically
// Persist cookies to a file (JSON format)
const persistent = new Rezo({
cookieFile: './cookies.json'
});
// Cookies are loaded from the file on construction
// and saved after each request automatically
// Or use Netscape format for curl/wget compatibility
const netscape = new Rezo({
cookieFile: './cookies.txt'
});
// Share a cookie jar between multiple instances
const jar = new CookieJar();
const client1 = new Rezo({ jar });
const client2 = new Rezo({ jar });
// Both clients see each other's cookies Streaming
The stream method returns a RezoStreamResponse that emits events as data arrives:
const stream = rezo.stream('https://api.example.com/events');
stream.on('start', (info) => {
console.log('Request started:', info);
});
stream.on('headers', (info) => {
console.log('Status:', info.status);
console.log('Headers:', info.headers);
});
stream.on('data', (chunk) => {
process.stdout.write(chunk);
});
stream.on('progress', (progress) => {
console.log(`Downloaded: ${progress.loaded} bytes`);
});
stream.on('finish', (info) => {
console.log('Stream complete:', info);
});
stream.on('error', (err) => {
console.error('Stream error:', err.message);
}); Downloads
Download files to disk with real-time progress tracking:
const download = rezo.download(
'https://releases.example.com/app-v2.zip',
'./downloads/app-v2.zip'
);
download.on('progress', (progress) => {
const pct = ((progress.loaded / progress.total) * 100).toFixed(1);
console.log(`${pct}% (${progress.loaded} / ${progress.total} bytes)`);
});
download.on('finish', (info) => {
console.log('Download complete:', info.path);
});
download.on('error', (err) => {
console.error('Download failed:', err.message);
}); Uploads
Upload files with progress tracking:
import { RezoFormData } from 'rezo';
import { readFileSync } from 'node:fs';
const fileBuffer = readFileSync('./report.pdf');
const upload = rezo.upload('https://api.example.com/files', fileBuffer, {
headers: { 'Content-Type': 'application/pdf' }
});
upload.on('progress', (progress) => {
console.log(`Uploaded: ${progress.loaded} bytes`);
});
upload.on('finish', (info) => {
console.log('Upload complete');
}); Request Queue
Control concurrency and rate limiting across requests:
import { Rezo } from 'rezo';
const client = new Rezo({
baseURL: 'https://api.example.com',
queue: {
concurrency: 5, // max 5 requests in flight
interval: 1000, // per-second window
intervalCap: 10 // max 10 requests per second
}
});
// All requests go through the queue
const promises = Array.from({ length: 100 }, (_, i) =>
client.get(`/items/${i}`)
);
const results = await Promise.all(promises); What’s Next
- Adapters — Learn about HTTP, HTTP/2, Fetch, XHR, cURL, and React Native adapters
- Stealth Mode — Browser fingerprint emulation for web scraping
- Crawler — Built-in site crawler with depth control and session management