RezoFormData
RezoFormData is a universal FormData wrapper built on the native FormData API. It works across Node.js 18+, Bun, Deno, browsers, Cloudflare Workers, and edge runtimes. It adds Buffer conversion, synchronous content-type access, object serialization with nested key support, and URL-encoded form creation.
import { RezoFormData } from 'rezo'; Basic Usage
const form = new RezoFormData();
// Append text fields
form.append('username', 'johndoe');
form.append('email', 'john@example.com');
// Append a file
const file = new Blob(['file content'], { type: 'text/plain' });
form.append('avatar', file, 'avatar.png');
// Use with Rezo
import rezo from 'rezo';
const response = await rezo.post('https://api.example.com/upload', form); Field Operations
append(name, value, filename?)
Add a field. Accepts string, Blob, or Buffer. Buffer values are automatically converted to Blob:
form.append('name', 'Alice');
form.append('photo', new Blob([imageData], { type: 'image/png' }), 'photo.png');
form.append('document', Buffer.from('PDF content'), 'report.pdf'); set(name, value, filename?)
Replace an existing field (or add if it does not exist):
form.set('name', 'Bob'); // Replaces previous 'name' value get(name) / getAll(name)
Retrieve field values:
form.get('name'); // 'Bob'
form.getAll('tags'); // ['js', 'typescript'] has(name) / delete(name)
form.has('name'); // true
form.delete('name');
form.has('name'); // false Iteration
for (const [key, value] of form) {
console.log(key, value);
}
form.forEach((value, key) => {
console.log(key, value);
}); Synchronous Content-Type
getContentType() returns the multipart/form-data content type with boundary synchronously. The boundary is eagerly generated at construction time:
const form = new RezoFormData();
form.append('field', 'value');
form.getContentType();
// 'multipart/form-data; boundary=----RezoFormBoundarya1b2c3d4e5f6...' This is critical for Rezo’s request pipeline, which needs the content type before the async serialization step. After the first async operation (toBuffer, toArrayBuffer, etc.), the cached content type reflects the actual boundary used by the native FormData serialization.
Async Content-Type
For the exact content type with the native boundary after serialization:
const contentType = await form.getContentTypeAsync(); Buffer Conversion
toBuffer() (Node.js/Bun/Deno)
const buffer = await form.toBuffer();
// Returns Node.js Buffer toArrayBuffer() (Universal)
const ab = await form.toArrayBuffer();
// Returns ArrayBuffer (works everywhere) toUint8Array() (Universal)
const bytes = await form.toUint8Array();
// Returns Uint8Array Synchronous Buffer Access
After any async conversion, the result is cached. Use getBuffer() and getLengthSync() for synchronous access:
await form.toBuffer(); // Triggers caching
form.getBuffer(); // Returns cached Buffer (null if not cached)
form.getLengthSync(); // Returns cached byte length (undefined if not cached) Async Length and Headers
const length = await form.getLength();
// Returns byte length as number
const headers = await form.getHeadersAsync();
// { 'content-type': 'multipart/form-data; boundary=...', 'content-length': '1234' } fromObject() — Object Serialization
Create a RezoFormData from a plain object. Handles primitives, arrays, nested objects, Blobs, Buffers, Uint8Arrays, ArrayBuffers, and file descriptors.
Default Mode (JSON-encoded nested values)
By default, nested objects and arrays are JSON-stringified:
const form = RezoFormData.fromObject({
name: 'Alice',
age: 30,
active: true,
tags: ['admin', 'user'],
address: { city: 'NYC', zip: '10001' },
});
// name=Alice, age=30, active=true
// tags=["admin","user"]
// address={"city":"NYC","zip":"10001"} Nested Key Mode (Bracket notation)
Pass nestedKeys: true to flatten with bracket notation (compatible with PHP, Rails, Express/qs):
const form = RezoFormData.fromObject({
user: {
name: 'Alice',
roles: ['admin', 'editor'],
address: { city: 'NYC' },
},
}, { nestedKeys: true });
// user[name]=Alice
// user[roles][0]=admin
// user[roles][1]=editor
// user[address][city]=NYC File Descriptors
Objects with value and filename/contentType properties are treated as file uploads:
const form = RezoFormData.fromObject({
document: {
value: Buffer.from('PDF content'),
filename: 'report.pdf',
contentType: 'application/pdf',
},
photo: {
value: new Blob([imageData]),
filename: 'photo.jpg',
},
}); Null/Undefined Handling
null and undefined values are silently skipped:
const form = RezoFormData.fromObject({
name: 'Alice',
nickname: null, // Skipped
bio: undefined, // Skipped
});
// Only 'name' field is included createUrlEncoded() — URL-Encoded Forms
Create a URL-encoded string for application/x-www-form-urlencoded POST bodies:
const body = RezoFormData.createUrlEncoded({
username: 'alice',
password: 's3cret',
remember: true,
});
// 'username=alice&password=s3cret&remember=true'
await rezo.post('https://example.com/login', body, {
headers: { 'content-type': 'application/x-www-form-urlencoded' },
}); fromNativeFormData() — Convert Native FormData
Wrap an existing native FormData instance:
const native = new FormData();
native.append('field', 'value');
native.append('file', someBlob, 'doc.pdf');
const form = RezoFormData.fromNativeFormData(native);
// Now has all the RezoFormData methods toNativeFormData()
Access the underlying native FormData when you need it for other APIs:
const native = form.toNativeFormData();
// Standard FormData object Query String Conversion
Convert string-only fields to URL query string or URLSearchParams:
form.append('q', 'search term');
form.append('page', '1');
form.toUrlQueryString();
// 'q=search+term&page=1'
form.toURLSearchParams();
// URLSearchParams { 'q' => 'search term', 'page' => '1' } Binary fields (Blob/File) are silently omitted from query string conversion.
Complete Example
import rezo, { RezoFormData } from 'rezo';
// Build a multipart form with file upload
const form = RezoFormData.fromObject({
title: 'Quarterly Report',
tags: ['finance', 'q4'],
attachment: {
value: Buffer.from(pdfBytes),
filename: 'report.pdf',
contentType: 'application/pdf',
},
});
const response = await rezo.post('https://api.example.com/documents', form, {
timeout: 30000,
});
console.log(response.data);