4

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?
1
  • 1
    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. Commented Mar 13, 2018 at 18:32

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.