Make a helper for AJAX requests using axios
In this blog post, we are going to go through the implementation of the helper function that is created for making AJAX requests using axios. Currently, the AJAX calls are made in a very random manner where the calls are written in the component itself and involves rewriting of headers, etc for the API call. Let us go through the implementation in the blog, which will standardise the way to make API calls in SUSI.AI web clients.
The primary changes are –
- Making a common helper for AJAX requests with the help of axios.
- Making a common file containing all the API calls across the project.
Going through the implementation
The API calls within the repository were not being made in an organised way, also a lot of redundant code was present. The aim of creating the helper is that, all the API calls is called via this common function. It takes care of the headers and also sending access_token with the API if the user is already logged in for API calls requiring authentication. The function for a API request now looks this simple –
// API call for signup export function getSignup(payload) { const { email, password } = payload; const url = `${API_URL}/${AUTH_API_PREFIX}/signup.json`; return ajax.get(url, { signup: email, password }); }
- In the above snippet, the ajax is the common helper used for making API calls. ajax is an object of functions that returns a promise for various methods of API requests
- We have primarily taken into consideration GET & POST requests type.
- The helper function is as follows –
/* Insert imports here*/ const cookies = new Cookies(); const obj = {}; ['get', 'post', 'all'].forEach(function(method) { obj[method] = function(url, payload, settings = {}) { /* Request will be aborted after 30 seconds */ settings = { timeout: 30000, dataType: 'json', crossDomain: true, ...settings, }; // Check if logged in if (cookies.get('loggedIn')) { payload = { access_token: cookies.get('loggedIn'), ...payload, }; } return new Promise(function(resolve, reject) { let methodArgs = []; if (method === 'post') { if (payload && payload instanceof FormData !== true) { // Convert to Form Data payload = toFormData(payload); } settings.headers = { 'Content-Type': 'application/x-www-form-urlencoded', ...settings.headers, }; } else if (method === 'get') { if (payload) { // Add params to the URL url += `?${Object.keys(payload) .map(key => key + '=' + payload[key]) .join('&')}`; } } const methodsToAxiosMethodsMap = { get: 'get', post: 'post', all: 'all', }; if (method === 'all') { methodArgs = [url]; } else if (method === 'get') { methodArgs = [url, settings]; } else { methodArgs = [url, payload, settings]; } axios[methodsToAxiosMethodsMap[method]].apply({}, methodArgs).then( function(data = {}, ...restSuccessArgs) { const statusCode = _.get(data, 'status'); /* Send only api response */ let responseData = { statusCode, ..._.get(data, 'data') }; if (method === 'all') { responseData = data; responseData.statusCode = statusCode; } if (payload) { responseData.requestPayload = payload; } // Mark the promise resolved and return the payload resolve(camelizeKeys(responseData), ...restSuccessArgs); }, function(data = {}, ...restErrorArgs) { // If request is canceled by user if (axios.isCancel(data)) { reject(data); } const statusCode = _.get(data, 'response.status', -1); let responseData = { statusCode, ..._.get(data, 'response.data') }; if (method === 'all') { responseData = data; responseData.statusCode = statusCode; } if (payload) { responseData.requestPayload = payload; } // Mark the promise rejected and return the payload reject(camelizeKeys(responseData), ...restErrorArgs); }, ); }); }; }); export default obj;
- The above objects contains 3 functions –
- ajax.get(url, payload, settings) – GET – The helper adds the query params to the URL by iterating through them and appending it to the URL and joining them with &.
- ajax.post(url, payload, settings) – POST – The helper checks, if the POST requests contains a payload which is an instance of form data, it converts to toFormData payload.
- ajax.all(url, payload, settings) – ALL – It helps to deal with concurrent requests.
- The url is the complete API endpoint, payload consists of the data/request payload. The settings consists of any headers related info, that needs to be added exclusively to the axios config.
- There is also no need to pass access token to each API request as a payload. The helper check whether the user is logged-in and adds the access_token to the request payload. The snippet below demonstrates it –
if (cookies.get('loggedIn')) { payload = { access_token: cookies.get('loggedIn'), ...payload, }; }
- The access token, if present in the cookies is added to the payload, therefore authenticating the API call.
- The keys of the response are changed to camel case before being sent to the caller function. It is done to maintain variable nomenclature standards across the app and also to follow javascript guidelines.
- A utils function for converting the object is added to convert the keys to camel case (Credits – https://github.com/substack/camelize).
- The file containing the API calls is structured as follows –
import ajax from '../helpers/ajax'; import urls from '../utils/urls'; const { API_URL } = urls; const AUTH_API_PREFIX = 'aaa'; const CHAT_API_PREFIX = 'susi'; const CMS_API_PREFIX = 'cms'; // API without request payload export function fetchApiKeys() { const url = `${API_URL}/${AUTH_API_PREFIX}/getApiKeys.json`; return ajax.get(url); } // API with request payload export function getLogin(payload) { const { email, password } = payload; const url = `${API_URL}/${AUTH_API_PREFIX}/login.json`; return ajax.get(url, { login: email, password, type: 'access-token' }); }
The above was the implementation of the helper function that is created for making AJAX requests using axios. I hope the blog provided a detailed insight of it helps in making the process of making API calls more standardised and easier.
References
- Camelizing the object – https://github.com/substack/camelize
- Axios documentation – https://github.com/axios/axios