Use Composition API to easily handle API requests in Vue.js
When building a web-app most of the times we need to fetch from the server or execute some action.
Handling the request status can be tedious and we often write the same code over and over.
With the composition API we can write a composable that handles the request status and exposes reactive objects
Let start by defining some requirements:
- Needs to provide reactive
isLoading,result,errorandexecuteisLoading:trueis we are waiting for a requestresult: Success response from the servererror: Error object form the serverexecute: Execute the request
- Need to provide the base request and the
executeshould allow sendingparamsorbodyto the request.
The expected usage will be like:
setup(){
const userList = useApi(()=>({url:`https://swapi.dev/api/people/1`}), (r)=> r.json());
userList.execute(); // we can await for result
// ...
return { ...userList }
}
This factory based usage allows great flexibility, the first parameter is a factory that returns the fetchRequest, the second parameter is just a transformer of the fetch result value.
The return execute() will pass all the parameters to the factory, allowing to greater customization on building requests.
Allowing you to do:
setup(){
const userList = useApi((page: number)=>({url:`https://swapi.dev/api/people/${page}`}), (r)=> r.json());
userList.execute(1); // we can await for result
// ...
return { ...userList }
}
Allowing greater composability on having automagically having a paginated list:
setup(){
// ...
const page = ref(1);
// watch for `page` changes
watch(page, p=> userList.execute(p));
// ...
return {
...userList,
page
}
}
Simplifying the pagination to changing variable page.value instead of calling the userList.execute(page.value).
# Implementation
A simple javascript implementation (with typings)
function useApi(
factory,
handleResponse
){
const isLoading = ref(false);
const result = ref(null);
const error = ref(null);
const execute = async (...args) => {
const request = factory(...args);
isLoading.value = true;
error.value = null;
try {
const response = await fetch(request);
const valueResponse = await handleResponse(response)
result.value = valueResponse;
return valueResponse;
} catch (e) {
error.value = e;
result.value = null;
} finally {
isLoading.value = false;
}
};
return {
isLoading,
result,
error,
execute,
};
}
This implementation might not yield correct results, if the
executeis called many times before the first finishes, because theresultwill always be the latest server response.
I'm the creator of a composable library compatible with vue2 + composition-api and vue3 called vue-composable, where you can do something similar by using usePromise, using it will make sure the result.value will hold the value of the last valid exec() response value.
// pass `true` on the second argument to make it lazy
const userList = usePromise((id)=>fetch(`https://swapi.dev/api/people/${id}`).then(r=>r.json(), true)
userList.exec(1)
I've been using this pattern on my projects, from action buttons to fetching the next paginated page, and has been working great so far. It makes it trivial to disable a button if a request is happening or to compose it into other composables.
Related Articles
How to use script setup in Nuxt 2
If you love Vue script setup and want to use it in your current Nuxt 2 project, the Nuxt team has made it possible to start using it today!
Feb 14, 2022
Create a i18n Plugin with Composition API in Vue.js 3
A example on how to use the inject and provide functions to create a i18n plugin using Composition API in Vue.js 3.
Feb 17, 2020
Access template refs in Composition API in Vue.js 3
Quick tip on how to access the old this.$refs by using ref() in the new Composition API in Vue.js 3 components.
Dec 9, 2019
Sponsors
VueDose is proudly supported by its sponsors. If you enjoy it, consider supporting it to ensure the project maintainability.

