Streamlining API Calls in Web Applications : A Comprehensive Guide

Roshan ShresthaRoshan Shrestha
4 min read

Introduction

In the early stages of my journey as a Frontend Web Developer, I encountered a recurring challenge: making API calls to fetch data from the server. While this task is essential to building interactive and data-driven web applications, I found myself writing the same code repeatedly in different components—whether using the native fetch API or libraries like Axios. Each time, I had to manually handle errors, manage loading states, and integrate responses into the UI. This process led my codebase complicated and made it harder to maintain and scale.

The Problem Statement: Repetitive Code and API Calls

Whether the project we have been working on or worked on is simple or complex, the data needs to be fetched from the different endpoints for different pages to power our website. During the process, we have been repeating the block of the API call function which makes our codebase difficult to maintain & scale. And, on the same hand, increases the complexity of our project.

The Solution: Centralized Web API Call

As I progressed, I discovered a better way to manage these API calls: centralizing them using custom hooks combined with TanStack Query. By creating custom hooks and leveraging useQuery for GET requests and useMutation for PUT, PATCH, and other methods, I was able to streamline API interactions, add caching, —all while keeping my code clean and maintainable.

Step-by-Step Guide to Implementing Centralized API Calls

  1. Building Custom Hooks for API Calls

    One of the most effective ways to centralize API calls is by creating custom hooks. Custom hooks allow you to encapsulate the logic for making API requests into reusable functions that can be shared across multiple components.

    I prefer creating a separate folder named apiServices and start creating the custom hook. Here’s how my Next.js folder structure looks like:

    Let’s take an example I have worked on in my project. Here, by centralizing the export functionality into the custom hook, we can easily manage the process of exporting data to Excel, handling any errors, and providing feedback to the user through a snackbar(toast) notification.

     import { useMutation } from "@tanstack/react-query";
     import { axioInstance } from "@/config/axiosConfig";
     //streamline custom hook to make api call
     const useExportToExcel = ({
       endpoint,
       mutationKey,
       params,
       fileNamePrefix,
       setSnackbarData,
     }) => {  
       const apiEndpoint = params ? `${endpoint}?${params}` : `${endpoint}`;   
       return useMutation(
         [...mutationKey],
         () => {
           return axiosInstance.get(apiEndpoint, {
             responseType: "blob", //necessary to handle binary response data
           });
         },
         {
           onSuccess: (response) => {
             // on success, we download the excel file, simply setting it in memory
             // buffer, creating a tag to get from buffer to our pc
             const blob = new Blob([response.data], {
               type: response.headers["content-type"],
             });
             const url = window.URL.createObjectURL(blob);
             const link = document.createElement("a");
    
             link.href = url;
             const fileName = `${fileNamePrefix}.xlsx`;
             link.setAttribute("download", fileName);
             document.body.appendChild(link);
             link.click();
    
             // Call your success snackbar or notification logic
             setSnackbarData({
               show: true,
               severity: "success",
               message: "Export successful",
             });
           },
           onError: (error) => {
             let err_msg = error?.response?.status;
    
             setSnackbarData({
               show: true,
               severity: "error",
               message:
                 err_msg
                   ? err_msg
                   : "Error: Export Unsuccessful !!!",
             });
           },
         }
       );
     };
     export default useExportToExcel;
    

    Parameter Breakdown

    To better understand how the useExportToExcel hook works, let’s break down the parameters:

    • endpoint (string): The API endpoint from which the Excel export data will be fetched.

      Example: '/api/export/excel'

    • mutationKey (string): A unique key to identify the mutation, used for caching or refetching.

      Example: 'export-excel-data'

    • params (object): The parameters to be sent with the request, typically as query or body data.

      Example: 'startDate=2024-01-07&endDate=2024-11-30'{endpoint}?startDate=2024-01-07&endDate=2024-11-30

    • fileNamePrefix (string): A prefix for the exported Excel file name, used to customize the file name.

      Example: 'example'example.xlsx

    • setSnackbarData (function): A toast function to handle setting data for a toast snackbar notification upon success or failure.

By centralizing the export functionality into this custom hook, you can easily manage the process of exporting data to Excel, handling any errors, and providing feedback to the user through a snack bar notification.

  1. Using the Custom Hook in a Component

    Implementation of custom hook in the component for API call;

import { useState } from "react";
import useExportToExcel from "@/api/export-services-hook/useExportToExcel";
import SnackbarComponent from "../../../shared/snackbar/SnackbarComponent";
import { ExcelExportApi } from "../../../../utils/apiUrls";

const ExportExcelComponent= () => {
  const [snackbarData, setSnackbarData] = useState({
    show: false,
    severity: "",
    message: "",
  });

  // custom hook for excel api call
  const { isLoading: excelLoading, mutate: exportExcelMutate } =
    useExportToExcel({
      mutationKey: ["export_due_list_excel"],
      endpoint: ExcelExportApi ,
      params: "startDate=2024-01-07&endDate=2024-11-30",
      fileNamePrefix: "excel",
      setSnackbarData: setSnackbarData,
    });
  const handleExcelExport = () => {
    exportExcelMutate();
  };

  return (
    <>
     {snackbarData.show && (
        <SnackbarComponent
          show={snackbarData.show}
          handleClose={handleSnackbarClose}
          data={snackbarData}
        />
      )}
    <h1> Demo on DownLoading Excel File </h1>
    <button onClick={handleExcelExport}> DownLoad Excel </Button>    
    </>
  );
};

export default ExportExcelComponent;

Summary

With the incorporation of streamline API call, it not make the code consistency, but also helps in scaling and maintaining as the project leaps to more complexity.


That’s all for the day.

Feel Free To Cross Question.

Thank You !!

0
Subscribe to my newsletter

Read articles from Roshan Shrestha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Roshan Shrestha
Roshan Shrestha

"Tech-savvy Computer Engineer with a passion for ML, NLP, and cloud solutions. AWS explorer and Full Stack problem solver—always up for a new tech challenge, one line of code at a time!"