Build an API Wrapper with VueJS & Axios

Part 4 of 4 in Your Guide to Using an API with VueJS/NuxtJS and Axios
Dan Pastori avatar
Dan Pastori November 16th, 2020

Creating 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!

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.