@tmlmobilidade/utils
Version:
A collection of utility functions and helpers for the TML Mobilidade Go monorepo, providing common functionality for batching operations, caching, HTTP requests, object manipulation, permissions, and more.
78 lines (77 loc) • 2.82 kB
JavaScript
/* * */
import { HttpResponse } from './response.js';
/**
* Fetches data from a URL with configurable HTTP method, body, headers, and options.
* @param url - The URL to fetch from
* @param method - The HTTP method to use (DELETE, GET, POST, PUT). Defaults to GET.
* @param body - Optional request body data
* @param headers - Optional request headers
* @param options - Optional fetch options (excluding body, headers, method)
* @returns Promise resolving to HttpResponse containing data, error and status
* @example
* ```ts
* // GET request
* const response = await fetchData<User>('/api/users/123');
*
* // POST request with body
* const response = await fetchData<User>(
* '/api/users',
* 'POST',
* { name: 'John', email: 'john@example.com' }
* );
* ```
*/
export async function fetchData(url, method = 'GET', body, headers = {}, options = {}, retries = 3) {
let attempt = 0;
let lastError;
while (attempt <= retries) {
try {
const response = await fetch(url, {
body: body ? JSON.stringify(body) : undefined,
credentials: 'include',
headers: {
...(method === 'GET' || method === 'DELETE' || 'Content-Type' in headers
? {}
: { 'Content-Type': 'application/json' }),
...headers,
},
method,
...options,
});
const data = (await response.json());
if (!response.ok || data.error) {
// Don't retry for 4xx (client) errors
if (response.status >= 400 && response.status < 500) {
return new HttpResponse({
data: null,
error: data.error,
statusCode: response.status,
});
}
throw new Error(`HTTP ${response.status} - ${data.error ?? 'Unknown error'}`);
}
return new HttpResponse({
data: data.data,
error: null,
statusCode: response.status,
});
}
catch (error) {
lastError = error;
attempt++;
// Stop retrying if we've reached the limit
if (attempt > retries)
break;
// Wait before retrying (exponential backoff: 0.5s, 1s, 2s, etc.)
const delay = 500 * Math.pow(2, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
return new HttpResponse({
data: null,
error: lastError instanceof Error
? `${lastError.message} - ${lastError.cause ?? ''}`
: 'Network error',
statusCode: 500,
});
}