What is the right way to make API calls with Vuex?

Nag picture Nag · Jan 29, 2018 · Viewed 10.7k times · Source

I have a Vue Webpack app with Vuex (I am new to both, coming in from the Ember world). I've currently got it setup to use vue-resource with two files like this:

/src/store/api.js

import Vue from 'vue';
import { store } from './store';

export default {
  get(url, request) {
    return Vue.http
      .get(store.state.apiBaseUrl + url, request)
      .then(response => Promise.resolve(response.body))
      .catch(error => Promise.reject(error));
  },
  post(url, request) {
    return Vue.http
      .post(store.state.apiBaseUrl + url, request)
      .then(response => Promise.resolve(response))
      .catch(error => Promise.reject(error));
  },
  // Other HTTP methods removed for simplicity
};

I then import the api.js file above into my /src/store/store.js file like this:

import Vue from 'vue';
import Vuex from 'vuex';
import Api from './api';

Vue.use(Vuex);

// eslint-disable-next-line
export const store = new Vuex.Store({
  state: {
    apiBaseUrl: 'https://apis.myapp.com/v1',
    authenticatedUser: null,
  },

  mutations: {
    /**
     * Updates a specific property in the store
     * @param {object} state The store's state
     * @param {object} data An object containing the property and value
     */
    updateProperty: (state, data) => {
      state[data.property] = data.value;
    },
  },

  actions: {
    usersCreate: (context, data) => {
      Api.post('/users', data)
        .then(response => context.commit('updateProperty', { property: 'authenticatedUser', value: response.body }))
        // eslint-disable-next-line
        .catch(error => console.error(error));
    },
  },
});

When I need to create a new user, I simply this.$store.dispatch('usersCreate', { // my data }); in my component. This works just fine but I have a few issues:

  1. I can't capture issues in the component to show toast messages etc. I can't even check if the AJAX call went through successfully.
  2. If I have a lot of APIs, I'll have to write a lot of actions in my store.js file which is not ideal. I could of course create a standard action that accepts the HTTP method, URL etc. and just call that but I am not sure if this is a good practice.

What is the right way to go about it? How can I check for the AJAX failure / success state in the component where I dispatch the action? What is the best practice to make API calls when using Vuex?

Answer

Matt picture Matt · Jan 29, 2018

Your action should return a Promise. Your current code just calls Api.post without returning it, so Vuex is out of the loop. See the example from Vuex docs for Composing Actions.

When you a return a Promise, the action caller can then follow the then() chain:

this.$store.dispatch('usersCreate').then(() => {
  // API success
}).catch(() => {
  // API fail
});

As for organizing your actions, you don't have to put them all in your store.js file. Vuex supports modules/namespacing. https://vuex.vuejs.org/en/modules.html