A wavey background

How to make API Calls with Vuex

Nick Basile • April 9, 2017

Today I'll explain how to make API calls with Vuex. While Vuex does a great job of keeping your data synchronized across the frontend of your application, you'll need to talk with a database at some point to make sure that any changes will be persisted permanently. Fortunately, Vue makes it very easy for us to work with an API, especially when we use Vue-resource. While Vue-resource is no longer part of the official Vue ecosystem, I think it is still an easy way to get up and going with APIs. But regardless of the API wrapper you choose to use, the concepts I describe here will still be applicable to your app.

The Big Picture

As we know from the Vuex docs, Vuex works like this:

Right away, we can see that the Vuex team has already told us to interact with a backend API via Actions. This is because Actions can be asynchronous, while Mutations must be synchronous. In this structure, it makes sense that Actions should be the place for API calls because they can wait for the data before committing to a Mutation. While this may seem a tad confusing conceptually, the big takeaway is to remember to only handle API calls in your Actions.

API Setup

Before we begin, I'd like to say thank you to jonagoldman on GitHub for outlining this setup. Now, let's jump into the code and see how to set up API calls in our Actions. As usual, I'll be working with a Laravel Spark project, but feel free to set up your directories as you see fit.

As you can see above, I have a vuex directory with two sub-directories for modules and utils as well as my top-level store.js. Inside of the utils directory, there is a file called api.js. This is a little helper file where I've wrapped some vue-resource calls within HTTP actions.

//resources/assets/js/vuex/utils/api.js

import Vue from 'vue'

export default {
    get(url, request) {
        return Vue.http.get(url, request)
            .then((response) => Promise.resolve(response.body.data))
            .catch((error) => Promise.reject(error));
    },
    post(url, request) {
        return Vue.http.post(url, request)
            .then((response) => Promise.resolve(response))
            .catch((error) => Promise.reject(error));
    },
    patch(url, request) {
        return Vue.http.patch(url, request)
            .then((response) => Promise.resolve(response))
            .catch((error) => Promise.reject(error));
    },
    delete(url, request) {
        return Vue.http.delete(url, request)
            .then((response) => Promise.resolve(response))
            .catch((error) => Promise.reject(error));
    }
}

Now, within an actions.js file, you can import api.js and use api.get(url, request) to make a request. This isn't a required step, but I find that it keeps my Actions more concise and readable. Plus, if I ever wanted to stop using vue-resource, I'd just need to edit this file and my Actions would be fine.

With that out of the way, let's hop into an actions.js file and see what your API calls can look like. Side note: your actions.js file can be for your Modules or for your top-level store, just remember to do your imports correctly.

API in Actions

Here's what a fully fleshed out actions.js might look like:

import api from '../../utils/api.js'

const actions = {
    getIncrementers (context) => {
        return api.get('/incrementers')
            .then((response) => context.commit('GET_INCREMENTERS', response))
            .catch((error) => context.commit('API_FAILURE', error));
    },
    createIncrementer (context, data) => {
        return api.post(data.url, data.request)
            .then((response) => context.commit('CREATE_INCREMENTER', response))
            .catch((error) => context.commit('API_FAILURE', error));
    },
    updateIncrementer (context, data) => {
        return api.patch(data.url, data.request)
            .then((response) => context.commit('UPDATE_INCREMENTER', response))
            .catch((error) => context.commit('API_FAILURE', error));
    },
    deleteIncrementer (context, url) => {
        return api.delete(url)
            .then((response) => context.commit('DELETE_INCREMENTER', response))
            .catch((error) => context.commit('API_FAILURE', error));
    }
};

export default actions

As you can see, we've used each of our HTTP actions from api.js - with some slight variations - to give you a complete picture of what you can do. Each of the variations are pretty self-explanatory, but let's run through them. In the getIncrementers function, you can see that we've hard-coded in the url. Typically, I prefer to pass in my url when I map the Action to my component, so I can handle any dynamic urls. But if the url is as simple as /incrementers, then you can probably get away with leaving it hard-coded. Now if you look at the rest of the function, you can see that since api.get() returns a Promise we can then chain on our Mutation commits. So if the API call succeeds, I'm committing to the GET_INCREMENTS Mutation and passing along the API response as well. And if the API call fails, I'm performing a different commit to API_FAILURE with the error so I can let the user know that something went wrong.

In the createIncrementer and updateIncrementer functions, we're handling things slightly differently. Since we need to pass some data to the API, we've included a data Object in the Action. From the data Object I can then get the url and the request and pass it to the API. Lastly, in the deleteIncrementer function, you can see that we're just passing in a url for the API to reference. This is how I typically handle the dynamic urls I was talking about earlier.

The Wrap Up

And there you have it! A quick look at how to get up and running with APIs and Vuex. With this in hand, and our articles on Modules and setting up Vuex, you should be well on your way to building some awesome apps with Vuex.