Features

Uploads

rezo.upload() sends data to a server with real-time progress tracking. It returns a RezoUploadResponse event emitter immediately, emitting progress, completion, and error events as the upload runs in the background.

Quick Start

import { Rezo } from 'rezo';
import { readFileSync } from 'node:fs';

const rezo = new Rezo();

const fileBuffer = readFileSync('./report.pdf');

const upload = rezo.upload('https://api.example.com/upload', fileBuffer);

upload.on('progress', (p) => {
  console.log(`${p.percentage.toFixed(1)}% uploaded`);
});

upload.on('finish', (info) => {
  console.log('Server response:', info.response.data);
  console.log('Upload speed:', (info.averageUploadSpeed / 1024).toFixed(0), 'KB/s');
});

upload.on('error', (err) => {
  console.error('Upload failed:', err.message);
});

Method Signature

rezo.upload(
  url: string | URL,
  data: Buffer | FormData | RezoFormData | string | Record<string, any>,
  options?: RezoHttpRequest
): RezoUploadResponse
  • url — the endpoint to upload to.
  • data — the upload payload. Accepts Buffer, FormData, RezoFormData, a string, or a plain object.
  • options — optional request configuration (headers, auth, proxy, timeout, etc.).

The method uses POST by default. Override via options.method if needed.

Data Types

Buffer

Upload raw binary data:

import { readFileSync } from 'node:fs';

const buffer = readFileSync('./image.png');
const upload = rezo.upload('https://api.example.com/images', buffer, {
  headers: { 'Content-Type': 'image/png' }
});

RezoFormData (Multipart)

Use RezoFormData for multipart uploads with file fields:

import { RezoFormData } from 'rezo';
import { readFileSync } from 'node:fs';

const form = new RezoFormData();
form.append('title', 'Quarterly Report');
form.append('file', readFileSync('./report.pdf'), 'report.pdf');
form.append('thumbnail', readFileSync('./thumb.jpg'), 'thumb.jpg');

const upload = rezo.upload('https://api.example.com/documents', form);

upload.on('finish', (info) => {
  console.log('Document ID:', info.response.data.id);
});

RezoFormData works across all environments (Node.js, Bun, Deno, browsers). It wraps the native FormData API with added convenience:

const form = new RezoFormData();

// Text fields
form.append('name', 'photo.jpg');
form.append('description', 'A sunset photo');

// File from Buffer
form.append('file', buffer, 'photo.jpg');

// File from Blob
form.append('file', new Blob([data], { type: 'image/jpeg' }), 'photo.jpg');

// Replace a field
form.set('name', 'updated-photo.jpg');

// Get the Content-Type with boundary (synchronous)
console.log(form.getContentType());
// multipart/form-data; boundary=----RezoFormBoundary...

Native FormData

You can also use the native FormData directly:

const form = new FormData();
form.append('file', new Blob([buffer]), 'data.csv');

const upload = rezo.upload('https://api.example.com/import', form);

String

Upload a raw string body:

const upload = rezo.upload('https://api.example.com/logs', logContent, {
  headers: { 'Content-Type': 'text/plain' }
});

Object

Upload a JSON object:

const upload = rezo.upload('https://api.example.com/data', {
  records: largeDataset,
  timestamp: Date.now()
});

Events Reference

initiated

Emitted when the request object has been created:

upload.on('initiated', () => {
  console.log('Upload request created');
});

start

Emitted when the upload begins:

upload.on('start', (info) => {
  console.log('Uploading to:', info.url);
  console.log('Method:', info.method);
});

headers

Emitted when the server’s response headers arrive:

upload.on('headers', (info) => {
  console.log('Response status:', info.status, info.statusText);
  console.log('Content-Type:', info.contentType);
});

status

Emitted with the HTTP response status:

upload.on('status', (status, statusText) => {
  console.log(`Server responded: ${status} ${statusText}`);
});

cookies

Emitted with response cookies:

upload.on('cookies', (cookies) => {
  cookies.forEach(c => console.log(`${c.key}=${c.value}`));
});

redirect

Emitted when a redirect is followed:

upload.on('redirect', (info) => {
  console.log(`Redirect: ${info.sourceUrl} -> ${info.destinationUrl}`);
});

progress

Emitted periodically during the upload:

upload.on('progress', (progress) => {
  console.log(`Uploaded: ${progress.loaded} / ${progress.total} bytes`);
  console.log(`Percentage: ${progress.percentage.toFixed(1)}%`);
  console.log(`Speed: ${(progress.speed / 1024).toFixed(0)} KB/s`);
  console.log(`ETA: ${(progress.estimatedTime / 1000).toFixed(1)}s`);
});

Progress fields:

PropertyTypeDescription
loadednumberBytes uploaded so far
totalnumberTotal bytes to upload
percentagenumber0—100
speednumberCurrent speed in bytes/second
averageSpeednumberAverage speed in bytes/second
estimatedTimenumberTime remaining in milliseconds
timestampnumberTimestamp of this event

finish / done

Emitted when the upload completes and the server responds:

upload.on('finish', (info) => {
  // Server response
  console.log('Response status:', info.response.status);
  console.log('Response body:', info.response.data);
  console.log('Response headers:', info.response.headers);

  // Upload metadata
  console.log('Upload size:', info.uploadSize, 'bytes');
  console.log('File name:', info.fileName);
  console.log('Final URL:', info.finalUrl);

  // Timing
  console.log('Total time:', info.timing.total, 'ms');
  console.log('Upload time:', info.timing.upload, 'ms');
  console.log('Server wait:', info.timing.waiting, 'ms');
  console.log('Upload speed:', (info.averageUploadSpeed / 1024).toFixed(0), 'KB/s');
});

done is an alias for finish.

error

Emitted if the upload fails:

upload.on('error', (err) => {
  console.error('Upload error:', err.code, err.message);
});

Upload Progress Bar

const form = new RezoFormData();
form.append('file', readFileSync('./backup.tar.gz'), 'backup.tar.gz');

const upload = rezo.upload('https://storage.example.com/backups', form);

upload.on('progress', (p) => {
  const bar = '='.repeat(Math.floor(p.percentage / 2)).padEnd(50);
  const speedKB = (p.speed / 1024).toFixed(0);
  process.stdout.write(`
[${bar}] ${p.percentage.toFixed(0)}% | ${speedKB} KB/s`);
});

upload.on('finish', (info) => {
  const sizeMB = (info.uploadSize / 1024 / 1024).toFixed(1);
  const timeSec = (info.timing.total / 1000).toFixed(1);
  console.log(`
Uploaded ${sizeMB} MB in ${timeSec}s`);
  console.log('Server:', info.response.status, info.response.data);
});

With Request Options

const upload = rezo.upload('https://api.example.com/files', fileBuffer, {
  headers: {
    'Authorization': 'Bearer token123',
    'X-Upload-Id': 'upload-abc'
  },
  timeout: 300_000, // 5 minute timeout for large files
  proxy: 'http://proxy.example.com:8080',
  maxRedirects: 3
});

Checking State

upload.isFinished(); // true after finish event
upload.url;          // The upload URL
upload.fileName;     // The file name (if available)
upload.status;       // HTTP status code (set after response)
upload.statusText;   // HTTP status text (set after response)