API calls in Vue.js
An alternative design for consuming APIs and mocking in a Vue project
Calling APIs can be cumbersome in web development. If you want to make calls to user-defined APIs from different components, you might end up with repeating code blocks. In such a case, you might either import same function or write the URL many times.
In Vue.js, developers can take advantage of user defined plugins. In a plugin you can inject properties to every Vue instance. In this example, we will inject $api property to every Vue instance and call APIs through the $api property. Before that, we will examine other ways of calling an API.
Calling API directly
APIs can be called directly with the fetch API on browsers ( or with Axios ). The downside of consuming an API this way is that you need to rewrite same URL every time you need to make a call to same API. If the URL changes, you need to change it in every component that you make the call inside.
In tests, you need to mock fetch in the global object and either write mock implementation or change return value before calling it.
Wrapping API call in a function
A better alternative is to do API call in a function and take variables that you need for the call as parameters. In this example, I created a js file for every REST resource and placed them under client folder.
vue-api-call/
├─ src/
│ ├─ clients/
│ │ ├─ people.js
│ │ ├─ planets.js
and I moved the fetch call to the function and took id as a parameter as seen below:
In order to make a call in a component, function should be imported and called as seen below. The advantage of this implementation is that we do not repeat code in API call and provide some level of abstraction.
What about testing? In test, since we imported a function from another file in the vue file, we have to mock the import. If we need to change the return value in the test; for example, we also need to import the function in test file to mock its return value.
Think of you have 5 or 10 different functions to mock in the test file. In that case, imports of functions and mocks of imports requires a lot of code.
Injecting functions to every Vue instance via plugin
To get rid of both mocks and imports in both Vue components and test, my suggestion is to write a plugin and inject functions to each Vue component through a property. In this example following plugin injects $api property to every Vue instance. Thanks to this plugin, every Vue instance can access to all function under the clients directory without any imports.
getCharacter method can now be simply called with $api property.
Now, there is no need to any mock of a import or any import of a function in tests. $api property can be mock as seen below:
Advantages
Abstraction
By this way, we can hide the implementation of API call. In Vue components, we do not know how the APIs are called, URL the request sent, and which libraries were used. Additionally, we can even return dummy data from the function in case that you have fixed data or your backend is not ready at the moment.
Less refactoring in case of a change
If URLs or return types of APIs change, we do not need to change any code in Vue components. Changing the functions under the client directory will be enough.
Less imports and mock in tests
You do not have to import any functions or mock any imports in test
Improvements
Functions in the Vue plugin can be preprocessed before injecting them. For example, base URL can be passed as an argument to functions or function calls can be intercepted if you want to call Vuex store before or after the call.
This implementation can be turned into a third party plugin which does the importing functions from the folder automatically. So in this case, user does not have to change the plugin every time there is a new resource.