Header Ads Widget

Ticker

6/recent/ticker-posts

Advanced Methods for Handling API Calls in JavaScript

Handling API Calls in JavaScript
Advanced Methods for Handling API Calls in JavaScript

You will need to know how you are making API calls. By doing things such as tracking how many calls you make, your application will operate better, smoother, and more reliably. In this article, we'll focus on some valuable techniques—sort of performance and user experience improvements. These include async/await, error handling, caching responses, working with paginated data, and debouncing API calls. Let's get started!

1. Cleaner Coding by async/await

async/await provides an elegant and readable way to manage asynchronous code as opposed to the conventional .then() and .catch() method-based promises. Fetch() yields a promise that makes the program look like a synchronization; with async/await, the program is more manageable to deal with.

So here’s an advanced approach to handling errors with async/await:

async function fetchData() {
  const apiUrl = 'https://jsonplaceholder.typicode.com/posts';
  
  try {
    const response = await fetch(apiUrl);
    
    // Check if the response is ok (status 200-299)
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    const data = await response.json();
    console.log(data); // Process the data
  } catch (error) {
    console.error('Error fetching data:', error.message);
  }
}

fetchData();

  • async is code that allows us to write asynchronous coding.
  • await is there to defer promise resolution and move to the next block statement.
  • Hence, in order to do both in the next program node, exceptions must lie as much as data processing errors. 

2. Handling API Rate Limits and Retry Logic

APIs generally come with rate limiters meant to control resource use. Certain situations instead require designing retry logic in relation to these rate-limited responses (e.g. 429 HTTP Too Many Requests) depending on the response status. if it is appropriate, the response status should be checked, and we should retry after a period of delay.

async function fetchWithRetry(url, retries = 3, delay = 1000) {
  try {
    const response = await fetch(url);
    
    if (!response.ok) {
      if (response.status === 429 && retries > 0) {
        console.log(`Rate limit exceeded. Retrying in ${delay / 1000} seconds...`);
        await new Promise(resolve => setTimeout(resolve, delay)); // Wait for retry delay
        return fetchWithRetry(url, retries - 1, delay * 2); // Exponential backoff
      }
      throw new Error(`Failed to fetch: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

fetchWithRetry('https://jsonplaceholder.typicode.com/posts')
  .then(data => console.log(data))
  .catch(error => console.error('Final error:', error));

  • The strategy adopts exponential backoff, i.e., each failed attempt increases the retry delay.
  • You can set the retries and delay count to customize retry behavior.
  • 429 (Too Many Requests) Error: This HTTP status is common in APIs to indicate rate limiting.

3. Caching API Responses

There are times when you don't need to hit the API every time the user needs the same data. In these cases, you can cache the API responses locally using localStorage or sessionStorage. That stores the API calls of the cache. By doing so, you can have fewer API hits, which improves performance and lessens the load on the server.
async function fetchWithCache(url) {
  const cachedData = localStorage.getItem(url);
  
  // If data is found in cache, return it
  if (cachedData) {
    console.log('Returning cached data');
    return JSON.parse(cachedData);
  }
  
  // If no cached data, fetch from API
  const response = await fetch(url);
  const data = await response.json();
  
  // Cache the data
  localStorage.setItem(url, JSON.stringify(data));
  console.log('Data fetched and cached');
  
  return data;
}

fetchWithCache('https://jsonplaceholder.typicode.com/posts')
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

  • This kind of data caching lasts even after the browser is closed.
  • A temporary type of caching, like sessionStorage, enables the old persisted values to be cleared away after the browser gets closed.
  • Non-static, or occasionally called, data can be cached for better statistics.

4. Pagination Handling:

Quite a few APIs return information in paginated form. For this type of data, you can expect the application to fetch from multiple pages, which could be performed as a manual "GET" by simply running "more" above the fold.
async function fetchPaginatedData(url) {
  let allData = [];
  let nextPageUrl = url;

  // Fetch data from all pages
  while (nextPageUrl) {
    const response = await fetch(nextPageUrl);
    const data = await response.json();
    
    // Append the current page's data
    allData = [...allData, ...data.results];
    
    // Check if there is a next page
    nextPageUrl = data.next; // `next` contains the URL for the next page
  }

  return allData;
}

fetchPaginatedData('https://jsonplaceholder.typicode.com/posts')
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

  • Paginated APIs would have to return a "next" link that the following set of data should possess. 
  • In other sense, the looped process of requesting for the next page should continue until there are no "next" pages present anymore. 
  • This ensures that one can get all data from an API that actually uses pagination.

5. Debouncing API Calls

This would be best for API calls in such cases when the users are busy typing into a search bar or clicking to interact with their input fields. Making an API call for every keystroke can destroy the server and create a worse, more thrilling experience by the user. Debouncing helps in this way: it postpones the API call until the user has stopped typing for a definite time.

let debounceTimeout;

function debouncedFetch(query) {
  // Clear the previous timeout if the user is still typing
  clearTimeout(debounceTimeout);

  // Set a new timeout to trigger the API call after 500ms
  debounceTimeout = setTimeout(async () => {
    const response = await fetch(`https://api.example.com/search?q=${query}`);
    const data = await response.json();
    console.log(data);
  }, 500); // 500ms delay before making the API call
}

// Example usage with an input field
document.getElementById('search').addEventListener('input', (event) => {
  debouncedFetch(event.target.value);
});

  • Delaying an API call is done through the `setTimeout()` function.
  • If a new request is entered before the delay expires, `clearTimeout()` cancels the previous request.
  • It makes the performance much better and helps not to make a call to the API on every user input.

CONCLUSION

Although you may find them easily manageable in JavaScript at the outset, they will demand defter techniques as your production application scales. These techniques include retry logic, caching, pagination, and debouncing to help bring improved performance and user experience. By making use of async/await functionality as well as managing rate limits for efficient handling of responses, you can create faster and scalable applications.
Thus, now you have a great idea far from your predecessors about setting and calling APIs within more complex, production-ready JavaScript applications.

Post a Comment

0 Comments