DEV Community

Ajmal Hasan
Ajmal Hasan

Posted on • Edited on

Building Reusable API Wrappers for Axios and Fetch

Here's an improved version of your blog post, focusing on readability, clarity, and structure:


Building Reusable API Wrappers for Axios and Fetch

In modern web development, handling API requests efficiently is crucial. To streamline this process, you can create reusable API wrappers for Axios and Fetch. These wrappers encapsulate common logic, making your code cleaner and more maintainable. Let's explore how to create and use these wrappers in your projects.


1. Axios API Wrapper

Axios is a powerful promise-based HTTP client for making requests. Here’s how you can set up a reusable Axios wrapper:

Configuration: urls.js

export const API_BASE_URL = 'https://jsonplaceholder.typicode.com';

export const getApiUrl = (endpoint) => API_BASE_URL + endpoint;

export const POSTS = getApiUrl('/posts');
export const DELETE_POSTS = getApiUrl('/todos/');
Enter fullscreen mode Exit fullscreen mode

Utility Functions: utils.js

In this file, we define reusable functions for making API requests with Axios. We also handle token management through AsyncStorage to add authorization headers.

import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
import store from '../redux/store';
import types from '../redux/types';

const { dispatch } = store;

export async function getHeaders() {
    let userData = await AsyncStorage.getItem('userData');
    if (userData) {
        userData = JSON.parse(userData);
        return {
            authorization: `${userData.access_token}`,
        };
    }
    return {};
}

export async function apiReq(endPoint, data, method, headers, requestOptions = {}) {
    return new Promise(async (res, rej) => {
        const tokenHeader = await getHeaders();
        headers = { ...tokenHeader, ...headers };

        if (method === 'get' || method === 'delete') {
            data = { ...requestOptions, ...data, headers };
        }

        axios[method](endPoint, data, { headers })
            .then(result => {
                const { data } = result;
                if (!data.status) return rej(data);
                res(data);
            })
            .catch(error => {
                console.error(error.response || error, 'API Error');
                handleAuthError(error);
                const errorMessage = error.response?.data?.message || "Network Error";
                rej({ message: errorMessage, ...error.response?.data });
            });
    });
}

function handleAuthError(error) {
    if (error.response?.status === 401) {
        clearUserData();
        // Navigate to Login page or reset navigation (uncomment as per your setup)
        // dispatch({ type: types.CLEAR_REDUX_STATE, payload: {} });
        // dispatch({ type: types.NO_INTERNET, payload: { internetConnection: true } });
    }
}

export function apiPost(endPoint, data, headers = {}) {
    return apiReq(endPoint, data, 'post', headers);
}

export function apiDelete(endPoint, data, headers = {}) {
    return apiReq(endPoint, data, 'delete', headers);
}

export function apiGet(endPoint, data, headers = {}, requestOptions) {
    return apiReq(endPoint, data, 'get', headers, requestOptions);
}

export function apiPut(endPoint, data, headers = {}) {
    return apiReq(endPoint, data, 'put', headers);
}

export async function clearUserData() {
    return AsyncStorage.removeItem('userData');
}
Enter fullscreen mode Exit fullscreen mode

Usage: action.js

You can now use the Axios wrapper to make API calls:

import { DELETE_POSTS, POSTS } from "../../config/urls";
import { apiDelete, apiGet } from "../../utils/utils";

export function getPosts() {
    return apiGet(POSTS);
}

export function deletePost(id) {
    return apiDelete(`${DELETE_POSTS}${id}`);
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • getHeaders(): Retrieves the stored access token from AsyncStorage and adds it to the headers.
  • apiReq(): Handles different HTTP methods (GET, POST, PUT, DELETE) and includes error handling for unauthorized responses.
  • handleAuthError(): Automatically clears user data and handles authentication errors like expired tokens.

2. Fetch API Wrapper

While Axios is popular, the Fetch API is a built-in alternative in JavaScript that can be just as powerful. Below is an example of creating a reusable wrapper for Fetch.

Fetch Utility:

import { stringify } from 'query-string';

export const sendRequest = ({
  url,
  method,
  useCredentials = false,
  body,
  headers = {},
  queryParams = {},
}) => {
  const options = {
    method: method,
    headers: new Headers({
      'content-type': 'application/json',
      ...headers,
    }), // Default content-type: JSON
    body: body ? JSON.stringify(body) : null,
  };

  if (useCredentials) options.credentials = 'include';
  if (queryParams) url = `${url}?${stringify(queryParams)}`;

  return fetch(url, options).then(res => {
    if (res.ok) {
      return res.json();
    } else {
      return res.json().then(json => {
        return Promise.reject({
          status: res.status,
          ok: false,
          message: json.message,
          body: json,
        });
      });
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • url: The API endpoint.
  • method: HTTP method (GET, POST, PUT, DELETE).
  • useCredentials: If you need to include credentials (e.g., cookies or sessions), set this to true.
  • body: The request payload (for POST/PUT).
  • headers: Additional headers can be passed, and the default content type is set to application/json.
  • queryParams: Any query parameters to append to the URL.

Example Usage:

const url = 'https://api.example.com/data';
sendRequest({
  url,
  method: 'GET',
  useCredentials: true,
  queryParams: {
    offset: 0,
    limit: 10,
  },
})
  .then((res) => console.log('Data fetched:', res))
  .catch((err) => console.error(`Error ${err.status}: ${err.message}`));
Enter fullscreen mode Exit fullscreen mode

Why Use a Wrapper?

  1. Reusability: You don’t need to rewrite the same logic for different API calls.
  2. Error Handling: Centralized error handling makes it easier to manage and debug.
  3. Authentication: Automatically includes authorization tokens or credentials where required.
  4. Customization: You can extend it to handle retries, timeouts, or even caching based on project requirements.

By creating API wrappers for Axios and Fetch, you simplify the process of making API requests, handling errors, and managing authentication. This structure allows for cleaner and more scalable code in your projects, improving both readability and maintainability.

Top comments (0)