Adaptive HTTP request system that automatically detects the best available HTTP client (fetch API or XMLHttpRequest) based on the environment. Provides consistent request handling across Node.js, browsers, and Deno with built-in error handling and retry logic.
Main request handler that automatically chooses the appropriate HTTP implementation.
/**
* HTTP request handler with automatic client detection
* Chooses between fetch API and XMLHttpRequest based on environment
* @param options - Backend configuration options
* @param url - Target URL for the request
* @param payload - Request payload (triggers POST if provided)
* @param callback - Result callback function
*/
function request(options: HttpBackendOptions, url: string, payload?: any, callback?: RequestCallback): void;
type RequestCallback = (error: any | undefined | null, response: RequestResponse | undefined | null) => void;
interface RequestResponse {
status: number;
data: any;
}Usage Examples:
import request from "i18next-http-backend/lib/request";
// GET request
request(
{ loadPath: '/api/translations/{{lng}}/{{ns}}' },
'/api/translations/en/common',
undefined,
(error, response) => {
if (error) {
console.error('Request failed:', error);
} else {
console.log('Status:', response.status);
console.log('Data:', response.data);
}
}
);
// POST request with payload
request(
{ addPath: '/api/translations/save' },
'/api/translations/save/en/common',
{ 'new.key': 'New value' },
(error, response) => {
console.log('Save result:', response);
}
);The request system automatically detects and uses the best available HTTP client:
// Automatic headers based on context
{
'User-Agent': 'i18next-http-backend (node/v18.0.0; linux x64)', // Node.js only
'Content-Type': 'application/json', // POST requests only
'X-Requested-With': 'XMLHttpRequest', // XMLHttpRequest only (non-cross-domain)
...customHeaders // User-provided headers
}Query parameters are automatically appended to URLs:
// Input URL: 'https://api.example.com/translations'
// Query params: { version: '2.0', format: 'json' }
// Result: 'https://api.example.com/translations?version=2.0&format=json'// XMLHttpRequest mode
{
crossDomain: true, // Omits X-Requested-With header
withCredentials: true // Includes cookies/credentials
}
// Fetch mode
{
requestOptions: {
mode: 'cors',
credentials: 'include' // Equivalent to withCredentials
}
}The request system provides consistent error handling across all HTTP clients:
Detects network failures and connection issues:
// Detected error messages (case-insensitive):
// - "failed"
// - "fetch"
// - "network"
// - "load"
// Results in error: "failed loading {url}: {originalMessage}"
// Retry: true (backend will retry these requests)"failed loading {url}; status code: {status}""failed loading {url}; status code: {status}""failed parsing {url} to json"Used in modern environments with fetch support:
// Request configuration
const fetchOptions = {
method: payload ? 'POST' : 'GET',
body: payload ? JSON.stringify(payload) : undefined,
headers: {
...customHeaders,
...(payload && { 'Content-Type': 'application/json' })
},
...requestOptions // User-provided fetch options
};
// Automatic fallback for unsupported request options
// Falls back gracefully if options cause "not implemented" errorsUsage Examples:
// Custom fetch options
const options = {
requestOptions: {
mode: 'cors',
credentials: 'include',
cache: 'no-cache',
redirect: 'follow'
}
};
// Dynamic fetch options
const options = {
requestOptions: (payload) => ({
method: payload ? 'POST' : 'GET',
mode: payload ? 'same-origin' : 'cors',
cache: payload ? 'no-cache' : 'default'
})
};Used as fallback in older browsers or when fetch is unavailable:
// Automatic configuration
xhr.open(payload ? 'POST' : 'GET', url, true); // Async by default
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // Unless crossDomain
xhr.withCredentials = !!options.withCredentials;
// Content type handling
if (payload) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
// MIME type override
if (xhr.overrideMimeType) {
xhr.overrideMimeType('application/json');
}Low-level fetch interceptor for testing or custom behavior:
type FetchFunction = (input: string, init: RequestInit) => Promise<Response> | void;
// Usage in options
{
alternateFetch: (url, fetchOptions) => {
console.log('Intercepting request to:', url);
// Return undefined to use default fetch
// Return Promise<Response> to handle request
return fetch(url, {
...fetchOptions,
headers: {
...fetchOptions.headers,
'X-Custom-Header': 'value'
}
});
}
}Special handling for
file://The request system includes automatic error recovery: