DEV Community

Cover image for Refactoring of API calls
Angha Ramdohokar
Angha Ramdohokar

Posted on

Refactoring of API calls

In my recent project, we have written multiple express APIs for different purposes and called them from react code. In this case multiple APIs have their different routes, definition and responses. Every api is having their CRUD operations and we have written separate code to call every api.

What this will lead to ? Code duplication and Code mess.
So I was thinking what I can do to avoid this mess and have a simple approach to call these APIs.

I spent time on analyzing the code we have written to call these APIs, what are the code duplication sections? can we make this generic any how?

As a result of analysis, I found that for every api call we have set of functions which can be minimized to generic ones and call for every API.

Following are set of things I have implemented for refactoring of API calls-

1. Division of code

In each place of API call, I found that we have performed CRUD (Create, Read, Update, Delete) operation only which can be moved to separate files and only difference is the name of resource e.g. /v1/api/users
/v1/api/companies

So users, companies are nothing but our resource the first part of api is same for all.
Keeping all these things in mind, we made following division-

api.provider.ts :
This file is having CRUD operation definition for API calls. It includes axios calling as its promise based and we can handle the responses the way we wanted.

// Define your api url from any source. Pulling from your .env   // file when on the server or from localhost when locally
const BASE_URL = Api_base_CRUD; 

/** @param {string} resource */ 
const getAll = (resource: string) => { 
  return axios 
    (`${BASE_URL}/${resource}`) 
    .then(resp => resp.data) 
    .catch(handleError); 
}; 

/** @param {string} resource */ 
/** @param {string} id */ 
const getSingle = (resource: string, id: string) => { 
  return axios 
    .get(`${BASE_URL}/${resource}/${id}`) 
    .then(resp => resp.data) 
    .catch(handleError); 
}; 

/** @param {string} resource */ 
/** @param {object} model */ 
const post = (resource: string, model: object) => { 
  return axios 
    .post(`${BASE_URL}/${resource}`, model) 
    .then(resp => resp.data) 
    .catch(handleError); 
}; 

/** @param {string} resource */ 
/** @param {object} model */ 
const patch = (resource: string, model: object) => { 
  return axios 
    .patch(`${BASE_URL}/${resource}`, model) 
    .then(resp => resp.data) 
    .catch(handleError); 
}; 

/** @param {string} resource */ 
/** @param {string} id */ 
const remove = (resource: string, id: AxiosRequestConfig<any> | undefined) => { 
  return axios 
    .delete(`${BASE_URL}/${resource}/${id}`, id) 
    .then(resp => resp.data) 
    .catch(handleError); 
}; 

Enter fullscreen mode Exit fullscreen mode

api.core.ts :
This is a class from where we can make calls to the provider file methods. Here we can pass the resource urls as well.

import {apiProvider} from './api-provider';

export class ApiCore {
  getAll!: () => any;
  getSingle!: (id: any) => any;
  post!: (model: any) => any;
  patch!: (model: any) => any;
  remove!: (id: any) => any;
  url!: string;

  constructor(options: { getAll: any; url: any; getSingle: any; post: any; patch: any; remove: any}) {
    if (options.getAll) {
      this.getAll = () => {
        return apiProvider.getAll(this.url);
      };
    }

    if (options.getSingle) {
      this.getSingle = (id) => {
        return apiProvider.getSingle(this.url, id);
      };
    }

    if (options.post) {
      this.post = (model) => {
        return apiProvider.post(this.url, model);
      };
    }

    if (options.patch) {
      this.patch = (model) => {
        return apiProvider.patch(options.url, model);
      };
    }

    if (options.remove) {
      this.remove = (id) => {
        return apiProvider.remove(this.url, id);
      };
    }

  }
}
Enter fullscreen mode Exit fullscreen mode

api.operation.ts :
This will be the actual file we will be making use of when making api calls, this includes making an object of api-core class and specify the parameters for the constructor.

import { ApiCore } from "./api-core";

const apiOperation = new ApiCore({
  getAll: true,
  getSingle: true,
  post: true,
  patch: true,
  remove: true,
  url: "",
});
export default apiOperation;
Enter fullscreen mode Exit fullscreen mode

2. Implementing API calls

Now its time to make calls to our api using the generic api files we have created.

import apiUsers from '../../api-operation';

function callUsersData(){
  apiUsers.url = "users";
  apiUsers.getAll()
  .then((resp:any) => {
    let user = resp.data?.rows; 
  })
}
Enter fullscreen mode Exit fullscreen mode

The only thing that will be different in each api is their url, everything else is generic now.

Conclusion :
By making division of files and using the generic functions for api call, now the code base looks simple, easy to read and mainly we removed code duplication.
I hope this helps you manage your API code structure easily manageable and understandable as your code base and team grows!

Here is a link of reference used while doing implementation :
https://dev.to/mmcshinsky/a-simple-approach-to-managing-api-calls-1lo6

Happy Reading :)

Top comments (2)

Collapse
 
marcomoscatelli profile image
Marco Moscatelli

Great article! It is very well written

Collapse
 
paresh4734 profile image
Paresh Awashank

Thanks for sharing!