JAVASCRIPT

Robust API Call Retries with Exponential Backoff

Implement an exponential backoff strategy for retrying failed API requests in JavaScript, improving application resilience and handling transient network issues or rate limits gracefully.

async function fetchDataWithRetry(url, options = {}, retries = 3, delay = 1000) {
  try {
    const response = await fetch(url, options);
    if (response.ok) {
      return await response.json();
    } else if (response.status === 429 || response.status >= 500 && retries > 0) {
      console.warn(`Attempt failed for ${url}. Retrying in ${delay / 1000}s... (Retries left: ${retries})`);
      await new Promise(resolve => setTimeout(resolve, delay));
      return fetchDataWithRetry(url, options, retries - 1, delay * 2); // Exponential backoff
    } else {
      const errorData = await response.json().catch(() => ({ message: response.statusText }));
      throw new Error(`API error: ${response.status} - ${errorData.message || response.statusText}`);
    }
  } catch (error) {
    if (retries > 0) {
      console.warn(`Network error for ${url}. Retrying in ${delay / 1000}s... (Retries left: ${retries})`);
      await new Promise(resolve => setTimeout(resolve, delay));
      return fetchDataWithRetry(url, options, retries - 1, delay * 2); // Exponential backoff
    } else {
      console.error(`Max retries exceeded for ${url}:`, error);
      throw error;
    }
  }
}

// Example usage:
(async () => {
  try {
    const data = await fetchDataWithRetry('https://api.example.com/data-that-might-fail', {}, 5);
    console.log('Successfully fetched data:', data);
  } catch (error) {
    console.error('Failed to fetch data after multiple retries:', error.message);
  }
})();

// Example usage with a mock failing API
let requestCount = 0;
async function mockFailingApi(url) {
    requestCount++;
    if (requestCount < 3) {
        console.log(`Mock API call ${requestCount}: Failing with 500`);
        return new Response(JSON.stringify({ message: 'Internal Server Error' }), { status: 500 });
    } else {
        console.log(`Mock API call ${requestCount}: Succeeding`);
        return new Response(JSON.stringify({ message: 'Success after retries', data: [1,2,3] }), { status: 200 });
    }
}

// Replace global fetch for demonstration purposes (do not do this in production)
// const originalFetch = global.fetch;
// global.fetch = mockFailingApi;

// (async () => {
//   try {
//     requestCount = 0; // Reset for this example
//     const data = await fetchDataWithRetry('https://mock-api.example.com/sometimes-fails', {}, 3);
//     console.log('Mock API - Successfully fetched data:', data);
//   } catch (error) {
//     console.error('Mock API - Failed after multiple retries:', error.message);
//   } finally {
//     global.fetch = originalFetch; // Restore original fetch
//   }
// })();
How it works: This JavaScript function provides a robust way to handle unreliable API calls using an exponential backoff retry strategy. When an API request fails due to server errors (5xx status codes) or rate limiting (429 status code), the function automatically retries the request after an increasing delay. This prevents overwhelming the server with immediate retries and significantly improves the application's resilience against transient network issues or temporary API unavailability, making your integrations more reliable. The `delay` is doubled with each retry, and `retries` specifies the maximum attempts.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs