3
votes

so i am creating spa web apps using laravel as my backend and vue js as the spa frontend framework. i am using laravel passport for authentication with api. and i am using vuex for controlling my application state.

first i create an api files to interact with api with axios

import { BKCU_CONFIG } from '../config.js';

export default {

  getArtikelS: function( p ){
    return axios.get( BKCU_CONFIG.API_URL + '/artikel' + `?column=${p.column}&direction=${p.direction}&per_page=${p.per_page}&page=${p.page}&search_column=${p.search_column}&search_operator=${p.search_operator}&search_query_1=${p.search_query_1}&search_query_2=${p.search_query_2}`);
  },

  getArtikelCUS: function( p, id ){
    return axios.get( BKCU_CONFIG.API_URL + '/artikel/indexCU/' + id + `?column=${p.column}&direction=${p.direction}&per_page=${p.per_page}&page=${p.page}&search_column=${p.search_column}&search_operator=${p.search_operator}&search_query_1=${p.search_query_1}&search_query_2=${p.search_query_2}`);
  },

  getArtikel: function( id ){
    return axios.get(BKCU_CONFIG.API_URL + '/artikel/' + id);
  },

  createArtikel: function(){
    return axios.get(BKCU_CONFIG.API_URL + '/artikel/create');
  },

  storeArtikel: function ( form ){
    return axios.post(BKCU_CONFIG.API_URL + '/artikel/store', form);
  },

  editArtikel: function( id ){
    return axios.get(BKCU_CONFIG.API_URL + '/artikel/edit/' + id);
  },

  updateArtikel: function ( id, form ){
    return axios.post(BKCU_CONFIG.API_URL + '/artikel/update/' + id, form);
  },

  updateTerbitkan: function( id ){
    return axios.post(BKCU_CONFIG.API_URL + '/artikel/updateTerbitkan/' + id);
  },

  updateUtamakan: function( id ){
    return axios.post(BKCU_CONFIG.API_URL + '/artikel/updateUtamakan/' + id);
  },

  deleteArtikel: function( id ){
    return axios.delete(BKCU_CONFIG.API_URL + '/artikel/' + id);
  }
}

and then i create a vuex module for each model ex. article like this:

import ArtikelAPI from '../../api/artikel.js';

export const artikel = {
  state: {
    artikelS: [],
    artikelLoadStatS: '',
    artikel: {},
    artikelLoadStat: '',
    artikelUpdate: '',
    artikelUpdateStat: '',
    artikelRules: [],
    artikelOption: [],
  },

  actions: {

    // load all
    loadArtikelS( { commit }, p ){
      commit('setArtikelLoadStatS', 'loading');

      ArtikelAPI.getArtikelS( p )
        .then( function( response ){
          commit('setArtikelS', response.data.model);
          commit('setArtikelLoadStatS', 'success');
        })
        .catch( error => {
          commit('setArtikelS', error.response);
          commit('setArtikelLoadStatS', 'fail');
        });
    },

    // load by cu
    loadArtikelCUS( { commit }, [p, id] ){
      commit('setArtikelLoadStatS', 'loading');

      ArtikelAPI.getArtikelCUS( p, id )
        .then( function( response ){
          commit('setArtikelS', response.data.model);
          commit('setArtikelLoadStatS', 'success');
        })
        .catch( error => {
          commit('setArtikelS', error.response);
          commit('setArtikelLoadStatS', 'fail');
        });
    },

    // load single data
    loadArtikel( {commit}, id ){
      commit('setArtikelLoadStat', 'loading');

      ArtikelAPI.getArtikel( id )
        .then( function( response ){
          commit('setArtikel', response.data );
          commit('setArtikelLoadStat', 'success');
        })
        .catch( error => {
          commit('setArtikelS', error.response);
          commit('setArtikelLoadStatS', 'fail');
        });
    },

    // create page
    createArtikel( {commit} ){
      commit('setArtikelLoadStat', 'loading');

      ArtikelAPI.createArtikel()
        .then( function( response ){
          commit('setArtikel', response.data.form );
          commit('setArtikelRules', response.data.rules);
          commit('setArtikelOption', response.data.option)
          commit('setArtikelLoadStat', 'success');
        })
        .catch( function(){
          commit('setArtikel', []);
          commit('setArtikelRules', []);
          commit('setArtikelOption', [])
          commit('setArtikelLoadStat', 'fail');
        });
    },

    // store data
    storeArtikel( {commit, state, dispatch}, form ){
      commit('setArtikelUpdateStat', 'loading');

      ArtikelAPI.storeArtikel( form )
        .then( function( response ){
          if(response.data.saved){
            commit('setArtikelUpdate', response.data);
            commit('setArtikelUpdateStat', 'success');
          }else{
            commit('setArtikelUpdateStat', 'fail');
          }
        })
        .catch(error => {
          if (error.response.status) {
            this.errors = error.response.data;
            commit('setArtikelUpdate', this.errors);         
          }else{
            commit('setArtikelUpdate', 'Oops terjadi kesalahan :(');
          }
          commit('setArtikelUpdateStat', 'fail');
        });
    },

    // edit page
    editArtikel( {commit}, id ){
      commit('setArtikelLoadStat', 'loading');

      ArtikelAPI.editArtikel( id )
        .then( function( response ){
          commit('setArtikel', response.data.form );
          commit('setArtikelRules', response.data.rules);
          commit('setArtikelOption', response.data.option)
          commit('setArtikelLoadStat', 'success');
        })
        .catch( function(){
          commit('setArtikel', []);
          commit('setArtikelRules', []);
          commit('setArtikelOption', [])
          commit('setArtikelLoadStat', 'fail');
        });
    },

    // update data
    updateArtikel( {commit, state, dispatch}, [id, form] ){
      commit('setArtikelUpdateStat', 'loading');

      ArtikelAPI.updateArtikel( id, form )
        .then( function( response ){
          if(response.data.saved){
            commit('setArtikelUpdate', response.data);
            commit('setArtikelUpdateStat', 'success');
          }else{
            commit('setArtikelUpdateStat', 'fail');
          }
        })
        .catch(error => {
          if (error.response.status) {
            this.errors = error.response.data;
            commit('setArtikelUpdate', this.errors);         
          }else{
            commit('setArtikelUpdate', 'Oops terjadi kesalahan :(');
          }
          commit('setArtikelUpdateStat', 'fail');
        });
    },
    updateArtikelTerbitkan( {commit, state, dispatch}, id ){
      commit('setArtikelUpdateStat', 'loading');

      ArtikelAPI.updateTerbitkan( id )
        .then( function( response ){
          if(response.data.saved){
            commit('setArtikelUpdate', response.data);
            commit('setArtikelUpdateStat', 'success');
          }else{
            commit('setArtikelUpdateStat', 'fail');
          }
        })
        .catch(error => {
          if (error.response.status) {
            this.errors = error.response.data;
            commit('setArtikelUpdate', this.errors);         
          }else{
            commit('setArtikelUpdate', 'Oops terjadi kesalahan :(');
          }
          commit('setArtikelUpdateStat', 'fail');
        });
    },
    updateArtikelUtamakan( {commit, state, dispatch}, id ){
      commit('setArtikelUpdateStat', 'loading');

      ArtikelAPI.updateUtamakan( id )
        .then( function( response ){
          if(response.data.saved){
            commit('setArtikelUpdate', response.data);
            commit('setArtikelUpdateStat', 'success');
          }else{
            commit('setArtikelUpdateStat', 'fail');
          }
        })
        .catch(error => {
          if (error.response.status) {
            this.errors = error.response.data;
            commit('setArtikelUpdate', this.errors);         
          }else{
            commit('setArtikelUpdate', 'Oops terjadi kesalahan :(');
          }
          commit('setArtikelUpdateStat', 'fail');
        });
    },

    // delete data
    deleteArtikel( {commit, state, dispatch}, id ){
      commit('setArtikelUpdateStat', 'loading');

      ArtikelAPI.deleteArtikel( id )
        .then( function( response ){
          if(response.data.saved){
            commit('setArtikelUpdate', response.data);
            commit('setArtikelUpdateStat', 'success');
          }else{
            commit('setArtikelUpdateStat', 'fail');
          }
        })
        .catch(error => {
          if (error.response.status) {
            this.errors = error.response.data;
            commit('setArtikelUpdate', this.errors);         
          }else{
            commit('setArtikelUpdate', 'Oops terjadi kesalahan :(');
          }
          commit('setArtikelUpdateStat', 'fail');
        });
    },

    // reset status
    resetArtikelUpdateStat( {commit} ){
      commit('setArtikelUpdateStat', '');
    }
  },

  mutations: {
    setArtikelS ( state, artikelS ){
      state.artikelS = artikelS;
    },
    setArtikelLoadStatS( state, status ){
      state.artikelLoadStatS = status;
    },
    setArtikel ( state, artikel ){
      state.artikel = artikel;
    },
    setArtikelLoadStat( state, status ){
      state.artikelLoadStat = status;
    },
    setArtikelUpdateStat( state, status ){
      state.artikelUpdateStat = status;
    },
    setArtikelUpdate( state, data ){
      state.artikelUpdate = data;
    },
    setArtikelRules( state, rules ){
      state.artikelRules = rules;
    },
    setArtikelOption( state, option ){
      state.artikelOption = option;
    }
  },

  getters: {
    getArtikelS( state ){
      return state.artikelS;
    },
    getArtikelLoadStatS ( state ){
      return state.artikelLoadStatS;
    },
    getArtikel( state ){
      return state.artikel;
    },
    getArtikelLoadStat ( state ){
      return state.artikelLoadStat;
    },
    getArtikelUpdateStat ( state ){
      return state.artikelUpdateStat;
    },
    getArtikelUpdate ( state ){
      return state.artikelUpdate;
    },
    getArtikelRules ( state ){
      return state.artikelRules;
    },
    getArtikelOption ( state ){
      return state.artikelOption;
    }
  }
}

and as you can see, each of my action contain an error catch and it only catch the error and show the message. I find some disadvantage in this way of handling things

maybe someone can help me to improve this kind of workflow, everything is working fine and very structured but i find the error catch is lacking for example:

  1. because i am using laravel then there is the time my token expired (because user leave their pc for a period of time or other things) so it will show an authenticated error message in not very user friendly way, i want to it just redirect user to login page to relogin and then back to the page user currently on
  2. i think i do a lot of repeating code in here, can i just make a one single error handling for all of this axios request?
2
You could write a generic interceptor, but the way you are doing right now has the advantage of each handling the error in a specific way. If you wrote a generic interceptor you would lose that.acdcjunior

2 Answers

4
votes

You could use some features from axios to get rid of this boilerplate

Use interceptors

 axios.interceptors.response.use(response => {
  return response.data;
 }, error => {
 if (error.response && error.response.data) {
  // handle your errors here.
  handleServerErrors(error.response.data);
 }
 return Promise.reject(error);
});

Use axios defaults

axios.defaults.timeout = 5000;
axios.defaults.baseURL = BKCU_CONFIG.API_URL

Don't concatenate the url anymore. Since you already set the default url, you could do

getArtikel: function( id ){
   return axios.get('/artikel/' + id);
}

Use ES6 features and axios params so you could turn this

getArtikelS: function( p ){
 return axios.get( BKCU_CONFIG.API_URL + '/artikel' + `?column=${p.column}&direction=${p.direction}&per_page=${p.per_page}&page=${p.page}&search_column=${p.search_column}&search_operator=${p.search_operator}&search_query_1=${p.search_query_1}&search_query_2=${p.search_query_2}`);
},

Into something more nice like this

   getArtikelS(p){
     return axios.get(`/artikel`, {params: p})
   }

Axios supports params as an object so you don't have to write weird query strings :)

A final tip: Don't store everything into vuex. If you don't need that data in more than 1 component, call the api directly in your components.

0
votes

You can use interceptor. But, the backend should care about errors and send the corresponding message to front end.

request.interceptors.response.use(response => {
  return response
}, error => { 
  //Put your logic here to manage the error message coming from BE

  return Promise.reject(error )
})

For full example see how i have implemented the whole axios instance in my app:

(I have used vuetify snackbar component to display the error message)

import axios from 'axios'
import { store } from "../../store/store"
import { eventBus } from "../main";

const request = axios.create({
  baseURL: 'http://localhost:8000'
})

request.interceptors.request.use(request => {
  const token = store.state.token
  if (token) {
    request.headers.Authorization = 'Bearer ' + token
  }
  return request

}, error => {
  return Promise.reject(error)
})

request.interceptors.response.use(response => {
  return response
}, error => { //getting error from backend and display it with snackbar(vuetify component)
   eventBus.$emit('snackBar', {
     text: error.response.data.message,
     snackbarColor: 'red darken-4'
   })

  return Promise.reject(error )
})

export default request

Example error message with Laravel:

return response()->json(["message" => "You have not access to perform this action"],403);