Build an API Wrapper with VueJS & Axios
Part 4 of 4 in Your Guide to Using an API with VueJS/NuxtJS and AxiosCreating an API wrapper using VueJS & Axios makes your API interfacing code extremely fluid, modular, and maintainable. Before we get started, those using NuxtJS should skip to the next tutorial. This will tutorial will ONLY work with VueJS and not within NuxtJS.
With that being said, so far we’ve installed Axios and got it to work with VueJS and configured Axios to work globally. We’ve also went through a few more complex requests like POST, PUT, & PATCH. In this tutorial we will abstract all of our API requests into wrapper modules. Before we get started, let’s begin with why.
Why Should We Do This?
Simply put, for code maintainability and re-usability. What we will be doing is taking all of our API requests for a specific resource and wrapping them in a module. If you need to make a request to an API endpoint using this module with a Vuex action, you can. If you need to make an API request in a component, you can do that as well. This process will work with your own API or a 3rd Party API.
However, the best part is, if you need to change a request in any way (like adding a header, upgrading API versions, etc), you update it once. This will update the request through your entire app! It’s really convenient and easy to use. In the next tutorial I’ll show you how to do this with NuxtJS if that’s what you are using. Let’s get started!
Step 1: Build a Front End API Directory
The way I approach development is to set up “buckets” (which are just folders) to house code and psuedo-structure before I start developing. Before I add any API wrapping modules, I always create an /api
directory at the root of my app. In this directory I will place all of my modules.
If you have a very complex API, feel free to add sub-directories to this as well. The more code organization, the better! Let’s add our first module.
Step 2: Add Your First Module
Let’s say we have an API focused around music. This API has endpoints that allow us to manage the songs resource. We can view all songs (GET
), create a song (POST
), update a song (PUT
), view a song (GET
) and delete a song (DELETE
). There are a variety of places in our application where we interact with this resource, so we want to build a module.
The first step is to add the file: /api/songs.js
. In this file, add the following code:
export default {
index( params ){
},
show( id ){
},
update( id, data ){
},
create( data ){
},
delete( id ){
}
}
If you’ve worked with Laravel in the past, this naming convention may look familiar. If you haven’t, you might be wondering what’s up with the names of these methods. Laravel uses a naming scheme for resource endpoints that I really enjoy implementing on both back and front end. If you want to read about it, check out their documentation on Resource Controllers.
You don’t have to use Laravel as your backend and you really don’t even have to use this naming format. These are just the methods that we will be implementing for each resource regardless of first party or third party. Each method will return an Axios request which is a promise. This way we can efficiently handle the request in context.
The index
Endpoint
When following a resource naming scheme, the index
endpoint will load all of a specific resource. It’s a GET
request that usually accepts parameters so you can filter/order your response. You don’t really want to load all of the resource when you call this route. Especially in larger systems where this could literally be over a million items, so these filters usually come in handy or maybe even be required.
Let’s take a look at our music app example. Say we want to load all of the songs, we will have to implement the index()
method. To do that, add the following code:
index( params ){
return axios.get( 'https://music.com/api/v1/songs', {
params: params
})
},
Remember, we set up Axios to be global!
This method accepts a single parameter which should be a JSON object containing any filters, or ordering data that you wish to send. This variable allows you to pass filters to your resource module in the form of a JSON object that Axios will turn into a query string for your API. Remember, sending a GET request with Axios, the second parameter of the Axios GET request is the configuration. This will build a nice query string to append to your request. Because of these filters, this tends to be the most complicated endpoint to create.
Let’s say you wanted to search for an artist and order by newest releases. You’d pass a JSON object like this:
{
"artist": "Red Hot Chili Peppers",
"order_by": "release_date",
"order_direction": "DESC"
}
Your query string will then be formatted like: ?artist=Red%20Hot%20Chili%20Peppers&order_by=release_date&order_direction=DESC
Let’s build out the rest of our endpoints for our module and then we can show the power of this module within VueJS.
The show
Endpoint
The show()
method is very similar to the index()
method since it is a GET
request. However, the show()
method should only return a single resource. In our example, this is a song. To implement this method simply add the following code:
show( id ){
return axios.get( 'https://music.com/api/v1/songs/'+id );
},
Since we are limiting what this endpoint is returning to a single resource, we don’t pass a params
variable like we did to the index()
method. The variable id
allows us to load an individual resource based on it’s unique identifier.
The create
Endpoint
This endpoint will handle the creation of a resource on the API, thus using the POST
method. As discussed in the last section, there are two ways to send data to a server with Axios, through JSON and through Form Data. Our API wrapper will account for this by accepting whatever version you throw at it. Let’s add the endpoint like this:
create( data ){
return axios.post( 'https://music.com/api/v1/songs', data );
},
The parameter data
will contain either a JSON object or a FormData object depending on if you need to send files. Either one works! When we implement this module in Step 3 & 4 you can see how fluid this will be to submit data to a server.
The update
Endpoint
Similar to the create
endpoint, the update
endpoint sends data to the API. However, since we are updating a resource, we need an additional parameter of id
to identify the specific resource we are updating. Depending on the use case, you should use PUT
or PATCH
for the method. Let’s say we are using PUT
. Our wrapper method should look like this:
update( id, data ){
return axios.put( 'https://music.com/api/v1/songs/'+id, data );
},
We build the URL to update the specific song defined by our id
parameter. Similar to the create
endpoint, the data
parameter can be either JSON or FormData. HOWEVER, if it is FormData, you will have to modify your method in certain circumstances (like with Laravel), to look like this:
update( id, data ){
data._method = 'PUT';
return axios.post( 'https://music.com/api/v1/songs/'+id, data );
},
I know Laravel specifically requires you to POST
any FormData to the server, but if you add _method
and set it to PUT
you can keep your resource controllers standardized and can respond to the request.
The delete
Endpoint
This is the last endpoint we will be implementing. It simply deletes a resource from the system. The only parameter it accepts is the id
of the resource we wish to delete:
delete( id ){
return axios.delete( 'https://music.com/api/v1/songs/' + id )
}
There we go! This is what our final API Wrapper should look like:
export default {
index( params ){
return axios.get( 'https://music.com/api/v1/songs', {
params: params
})
},
show( id ){
return axios.get( 'https://music.com/api/v1/songs/'+id );
},
update( id, data ){
return axios.put( 'https://music.com/api/v1/songs/'+id, data );
},
create( data ){
return axios.post( 'https://music.com/api/v1/songs', data );
},
delete( id ){
return axios.delete( 'https://music.com/api/v1/songs/' + id )
}
}
Now, let’s get to the part where our hard work pays off. Using the module within a VueJS component and Vuex module! You will be able to use these methods anywhere you feel is necessary making the code standardized and extremely re-usable!
Step 3: Using Your API Wrapper in a VueJS Component
Now it’s time to reap the rewards of our hard work! We can re-use our module in any component or page within our VueJS App! The API Wrapper comes in handy whenever you need to interface with an API. Sometimes, this could be in a parent page (like a layout) as well where you need to load data globally.
All you need to do inside of your component is include the API module you created. Let’s say we have a page that loads all of the songs from our music app by the Red Hot Chili Peppers
. We would have a component that looks like this:
<template>
<div class="songs-page">
</div>
</template>
<script>
import SongsAPI from '../path/to/api/songs.js';
export default {
data(){
return {
songs: []
}
},
mounted(){
SongsAPI.index({
"artist": "Red Hot Chili Peppers",
"order_by": "release_date",
"order_direction": "DESC"
}).then(function( response ){
this.songs = response.data;
}.bind(this));
}
}
</script>
All we needed to do is import
our API module within our component and then call the method in our mounted()
lifecycle hook. You can also call this within a method if you want. Like if you had a button to load the songs you’d place the call to the API in the click handler method.
Our API wrapper simply returns the promise from the Axios request so we can listen to success and error responses and handle them accordingly.
Not only is the syntax more fluid and modular, it’s really easy to update all requests if necessary. Say we wanted to add authorization header to a specific API or we are working with 2 APIs that require slightly different configuration (foreshadowing an article coming soon 😉 ), or the API releases v2
. We can manage the requests modularly and update the request in a single location.
When sending data with your API request to create a resource, the syntax is much more fluid as well. This page will submit a song to save to your API:
<template>
<div class="add-song-page">
</div>
</template>
<script>
import SongsAPI from '../path/to/api/songs.js';
export default {
data(){
return {
form: {
name: '',
artist: '',
album: ''
}
}
},
methods: {
save(){
SongsAPI.create( this.form )
.then( function( response ){
// Handle success
})
}
}
}
</script>
Once again, the syntax is much more fluid! This also makes your forms more flexible. Say you add another field and have 3 different areas where you can quickly add a song. You wouldn’t have to add another variable to each request, you just submit this.form
which passes it to our wrapper and submits it!
Now let’s say you are building a large SPA and Vuex is heavily integrated into your app. You can use the same API wrapper within Vuex!
Step 4: Using Your API Wrapper in Vuex
Yup, these wrappers work fluidly within Vuex as well meaning you can trigger actions, that make API requests, and mutate state!
Let’s say you have an app like Spotify, but use an awesome audio player like AmplitudeJS (shameless plug). You look at the page that loaded all of your songs, but need to load an individual song from the API and save it to your Vuex state so it’s active across any page in your SPA and can be accessed at any time. This is a perfect use case for Vuex since it holds state across your app. It’s also a perfect way to show how to use your API wrapper to work within a Vuex module. Here’s our songs
Vuex module:
import SongsAPI from '../path/to/api/songs.js';
export const songs = {
state: {
nowPlaying: {}
},
actions: {
loadSong( { commit }, id ){
SongsAPI.show( id )
.then( function( response ){
commit( 'setNowPlaying', response.data );
})
.catch( function(){
commit( 'setNowPlaying', {} );
});
},
},
mutations: {
setNowPlaying( state, song ){
state.nowPlaying = song;
}
},
getters: {
getNowPlaying( state ){
return state.nowPlaying;
}
}
}
Looks like a lot, but Vuex modules are very verbose. So how this works is, we first import our songs API Wrapper. This allows us to have access to all of the methods that we just created.
Now, we set a piece of state for nowPlaying
which should be a JSON object that we will store the song we are playing. The core of the Vuex module is the actions
that we added. Specifically the loadSong( { commit }, id )
action. This is the action that uses our API wrapper to load a song from our songs API. This can be called from any page or component using this.$store.dispatch('loadSong', idOfSong);
When this action is called, we pass a specific ID and we call the show()
method on our SongsAPI
wrapper which accepts the ID as a parameter. On success, we commit
the mutation setNowPlaying
with the result from our API which in-turn sets the state in the app.
Now, whenever we want to access our now playing song, we just set a computed component in our component or page like this:
computed: {
nowPlaying(){
return this.$store.getters.getNowPlaying;
},
},
That’s it! Now you can re-use your API requests within Vuex modules! The only warning is the code for your Vuex module might look different if you are namespacing it. Other than that, you have a re-usable API wrapper that provides modular re-usable code within your application.
Conclusion
Personally, I believe building these API wrappers with Axios not only organizes code, but also speeds up bug fixes and changes when updates occur. This is by far my favorite way to interface with an API whether it’s your own or a 3rd Party.
Now if you are using NuxtJS, you might be wondering how to do this within your app. No worries, It’s extremely similar and just as easy! That’s next!
If you have any questions, critiques, messages, make sure to leave a comment on our community and I’ll lend a hand! You can also reach out to me on Twitter (@danpastori).
Want to see a lot more examples and how these wrappers and API requests work on both backend AND frontend? Check out our book, The Ultimate Guide to Building APIs & SPAs with Laravel and VueJS! You can see how we made ROAST which is 100% API driven and all resources are wrapped within API modules.
Next up, let’s go through API wrappers with NuxtJS!