const MAX_RETRIES = 5; // Maximum number of retries const BASE_DELAY_MS = 1000; // 1 second base delay async function fetchWithRetry( url: string, options?: RequestInit, retries: number = 0 ): Promise { try { const response = await fetch(url, options); if (response.status === 429) { // Too Many Requests if (retries < MAX_RETRIES) { const retryAfter = response.headers.get('Retry-After'); let delay = BASE_DELAY_MS * Math.pow(2, retries); // Exponential backoff if (retryAfter) { const retryAfterSeconds = parseInt(retryAfter, 10); if (!isNaN(retryAfterSeconds)) { delay = Math.max(delay, retryAfterSeconds * 1000); // Respect server's suggestion } } console.warn(`Rate limit hit. Retrying in ${delay / 1000} seconds... (Attempt ${retries + 1}/${MAX_RETRIES})`); await new Promise(resolve => setTimeout(resolve, delay)); return fetchWithRetry(url, options, retries + 1); // Recursive retry } else { throw new Error('Maximum retry attempts exceeded for rate limiting.'); } } if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status} - ${response.statusText}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return await response.text() as T; } catch (error) { console.error('Fetch operation failed:', error); throw error; } } // Usage example: async function loadData() { try { const data = await fetchWithRetry<{ message: string }>('https://api.example.com/sensitive-endpoint'); console.log('Data fetched successfully:', data); } catch (error) { console.error('Failed to load data after retries:', error); } } loadData();