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/');
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');
}
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}`);
}
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,
});
});
}
});
};
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}`));
Why Use a Wrapper?
- Reusability: You don’t need to rewrite the same logic for different API calls.
- Error Handling: Centralized error handling makes it easier to manage and debug.
- Authentication: Automatically includes authorization tokens or credentials where required.
- 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)