Skip to content

Instantly share code, notes, and snippets.

@michaelmcshinsky
Last active December 8, 2023 13:51
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save michaelmcshinsky/2560d9494b241afce230d9c6f5de6448 to your computer and use it in GitHub Desktop.
Save michaelmcshinsky/2560d9494b241afce230d9c6f5de6448 to your computer and use it in GitHub Desktop.
A comprehensive starter solution to making API calls structured and manageable in code architecture and development teams.
import apiProvider from './provider';
export class ApiCore {
constructor(options) {
if (options.getAll) {
this.getAll = () => {
return apiProvider.getAll(options.url);
};
}
if (options.getSingle) {
this.getSingle = (id) => {
return apiProvider.getSingle(options.url, id);
};
}
if (options.post) {
this.post = (model) => {
return apiProvider.post(options.url, model);
};
}
if (options.put) {
this.put = (model) => {
return apiProvider.put(options.url, model);
};
}
if (options.patch) {
this.patch = (model) => {
return apiProvider.patch(options.url, model);
};
}
if (options.remove) {
this.remove = (id) => {
return apiProvider.remove(options.url, id);
};
}
}
}
import axios from 'axios';
import { handleResponse, handleError } from './response';
// Define your api url from any source.
// Pulling from your .env file when on the server or from localhost when locally
const BASE_URL = 'http://127.0.0.1:3333/api/v1';
/** @param {string} resource */
const getAll = (resource) => {
return axios
.get(`${BASE_URL}/${resource}`)
.then(handleResponse)
.catch(handleError);
};
/** @param {string} resource */
/** @param {string} id */
const getSingle = (resource, id) => {
return axios
.get(`${BASE_URL}/${resource}/${id}`)
.then(handleResponse)
.catch(handleError);
};
/** @param {string} resource */
/** @param {object} model */
const post = (resource, model) => {
return axios
.post(`${BASE_URL}/${resource}`, model)
.then(handleResponse)
.catch(handleError);
};
/** @param {string} resource */
/** @param {object} model */
const put = (resource, model) => {
return axios
.put(`${BASE_URL}/${resource}`, model)
.then(handleResponse)
.catch(handleError);
};
/** @param {string} resource */
/** @param {object} model */
const patch = (resource, model) => {
return axios
.patch(`${BASE_URL}/${resource}`, model)
.then(handleResponse)
.catch(handleError);
};
/** @param {string} resource */
/** @param {string} id */
const remove = (resource, id) => {
return axios
.delete(`${BASE_URL}/${resource}`, id)
.then(handleResponse)
.catch(handleError);
};
export const apiProvider = {
getAll,
getSingle,
post,
put,
patch,
remove,
};
export function handleResponse(response) {
if (response.results) {
return response.results;
}
if (response.data) {
return response.data;
}
return response;
}
export function handleError(error) {
if (error.data) {
return error.data;
}
return error;
}
const url = 'tasks';
const plural = 'tasks';
const single = 'task';
// plural and single may be used for message logic if needed in the ApiCore class.
const apiTasks = new ApiCore({
getAll: true,
getSingle: true,
post: true,
put: false,
patch: true,
delete: false,
url: url,
plural: plural,
single: single
});
apiTasks.massUpdate = () => {
// Add custom api call logic here
}
export apiTasks;
import React, { useEffect } from 'react';
import { apiTasks } from '@/services/api';
export function Tasks() {
const [tasks, setTasks] = useState([]);
useEffect(() => {
_getTasks();
}, []);
function _getTasks() {
apiTasks.getAll().then((res) => {
let arr = _parseTasks(res.results.data);
setTasks(arr);
});
}
function _parseTasks(tasks) {
return tasks.map((task) => {
// Parse task information
return task;
});
}
function _createTask(task) {
apiTasks.post(task).then((res) => {
// state logic
});
}
function _updateTask(task) {
apiTasks.patch(task).then((res) => {
// state logic
});
}
function _removeTask(id) {
apiTasks.remove(id).then((res) => {
// state logic
});
}
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.name}</li>
))}
</ul>
);
}
@kevinpallado
Copy link

Hi, can you provide example for the customAPI call?

@abdes-zakari
Copy link

Hi, can you provide example for the customAPI call?

up

@michaelmcshinsky
Copy link
Author

task-api-example.js has one. You can add your axios logic there.

@ranjansaga
Copy link

For both success and failure cases, It is reaching .then() in all the calling places. Do we need a interceptor to return Promise.reject(error) ?

@michaelmcshinsky
Copy link
Author

That is normally an issue with the server, not the JavaScript. Most likely your server is sending back incorrect status codes.

@ranjansaga
Copy link

ranjansaga commented Aug 23, 2021

Hi Michael, Thanks for the quick response. I have created a simple react app which fetches list of users and displays it. I have used the above 'api-provider.js' and 'response.js' files to create a service layer. Here When i make a get call to get all the users. I see that the log inside 'then' block is seen.

fetchItems = () => {
  return apiProvider.getAll(url).then(function (userData) {
    console.log('inside then block'); // Control reaches here
    return userData;
  }).catch((error)=>{
    console.log('inside catch block');
  });
};

I am returning 400 error code in my back end. Also the catch block of getAll() function is getting called.

/** @param {string} resource */ 
const getAll = (resource) => { 
  return axios 
    .get(`${BASE_URL}/${resource}`) 
    .then(handleResponse) 
    .catch(handleError);  // Control reaches here
};

Is this the expected behaviour ? How do I make sure that the catch block in the fetchItems function gets called and it does not go inside then?

@michaelmcshinsky
Copy link
Author

Looks like this is a feature of the Axios library. You'll probably have to implement your own interceptor for axios or a check to throw certain statuses to your .catch method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment