unxhr: XMLHttpRequest Emulation for Node.js
`unxhr` is a Node.js library that provides an emulation of the browser's native `XMLHttpRequest` object, enabling both synchronous and asynchronous HTTP requests within Node.js environments. Currently at version 1.2.0, its release cadence is moderate, with recent updates primarily focused on bug fixes and infrastructure improvements. The project is a fork of the original `XMLHttpRequest` package, specifically developed to achieve compliance with the XMLHttpRequest Level 2 specifications. Key differentiators include its complete lack of external dependencies, support for standard HTTP methods (GET, POST, PUT, DELETE), handling of binary data through JavaScript typed arrays, automatic redirection following, and limited support for the `file://` protocol. It serves as a bridge for codebases that expect browser-like XHR behavior in a Node.js context, despite some inherent limitations compared to a full browser implementation.
Common errors
-
ReferenceError: XMLHttpRequest is not defined
cause The `XMLHttpRequest` class was not correctly imported or referenced before use.fixEnsure you are using `const XMLHttpRequest = require('unxhr').XMLHttpRequest;` and that `XMLHttpRequest` is in scope where you are attempting to instantiate it. -
Error: stdout maxBuffer exceeded. Did you mean to enable the sync option? Running an async process and expecting stdout to be captured. Consider increasing the maxBuffer option.
cause The HTTP response body exceeded the maximum buffer size allocated for the internal child process communication. This typically happens with large responses in older versions or if `UNXHR_MAX_BUFFER` is set too low.fixFor `unxhr` v1.2.0 and above, the default `maxBuffer` is 100MB. If you encounter this, increase the buffer size by setting the `UNXHR_MAX_BUFFER` environment variable (e.g., `UNXHR_MAX_BUFFER=200000000` for 200MB). -
Error: Network error or connection refused.
cause The `onerror` callback was triggered, indicating a network-level issue such as no internet connection, an unreachable host, or a blocked port.fixVerify network connectivity, check the target URL/host for correctness, and ensure no firewalls or proxies are blocking the request. Inspect the `onerror` event for more specific details if available.
Warnings
- breaking Synchronous requests using `unxhr` will block the entire Node.js event loop until a response is received. This behavior is inherent to synchronous XHR and can lead to unresponsive applications and performance issues.
- gotcha Synchronous requests in `unxhr` are known to have issues with properly setting and handling HTTP headers, potentially leading to incorrect request behavior or unexpected server responses.
- gotcha Accessing local files via the `file://` protocol may produce unexpected results or errors, especially when dealing with files that are not encoded in UTF-8. There are known limitations in handling these scenarios.
- gotcha The `unxhr` implementation is an emulation and does not provide a complete feature set identical to a browser's native `XMLHttpRequest`. Key missing features include certain events (e.g., `abort`), persistence of cookies between requests, and robust XML parsing support.
Install
-
npm install unxhr -
yarn add unxhr -
pnpm add unxhr
Imports
- XMLHttpRequest
import { XMLHttpRequest } from 'unxhr';const XMLHttpRequest = require('unxhr').XMLHttpRequest; - XHR instance
const xhr = unxhr.XMLHttpRequest();
const xhr = new XMLHttpRequest();
Quickstart
const XMLHttpRequest = require('unxhr').XMLHttpRequest;
async function makeRequest() {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true); // true for async
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
reject(new Error('Failed to parse JSON response: ' + e.message));
}
} else {
reject(new Error(`Request failed with status ${xhr.status}: ${xhr.statusText}`));
}
}
};
xhr.onerror = () => {
reject(new Error('Network error or connection refused.'));
};
xhr.send();
});
}
makeRequest()
.then(data => console.log('Fetched data:', data))
.catch(error => console.error('Error during request:', error));
// Example of a synchronous request (use with extreme caution due to blocking nature)
// console.log('\nStarting synchronous request (will block event loop)...');
// const syncXhr = new XMLHttpRequest();
// try {
// syncXhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', false); // false for sync
// syncXhr.send(null);
// if (syncXhr.status === 200) {
// console.log('Synchronous Response:', JSON.parse(syncXhr.responseText));
// } else {
// console.error('Synchronous Error:', syncXhr.status, syncXhr.statusText);
// }
// } catch (e) {
// console.error('Synchronous Request Failed:', e);
// }
// console.log('Synchronous request finished.');