LordGrim
LordGrim

Reputation: 751

Best Practice in Error Handling in Vuejs With Vuex and Axios

I am using Vuex + axios, I want to know the best practice in handling errors for vuex + axios. What I am doing now is that when I request using axios and it returns an error, it will be committed in mutation and update my state. What I want to do is, If there's an response error from my request it will return to my component so that I can handle the error much faster.

Like in angular, there's a dependency injection and the response will return to the component.

Upvotes: 29

Views: 60058

Answers (8)

Himanshu sharma
Himanshu sharma

Reputation: 7949

Let me tell you the approach, I used for error logging is this. By this you can handle all vue error by on code.

window.onerror = function (message, source, lineno, colno, error) {
  /// what you want to do with error here
};

This is a global error handler for the browser. If any error comes uncaught that can be handle by this.

Also, if you want to handle your error. You can do this.

axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
      // when you throw error this will also fetch error.
       throw error;
  });

If you want to look on vue for error handling you can go for. https://v2.vuejs.org/v2/api/#errorHandler

Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  // `info` is a Vue-specific error info, e.g. which lifecycle hook
  // the error was found in. Only available in 2.2.0+
}

Let me give you a link where window.onerror is used

https://github.com/stacktracejs/stacktrace.js/

Upvotes: 5

Ahmed Shehab
Ahmed Shehab

Reputation: 1877

Now available in VUE 3 composition API uniquely tackled

we might use AXIOS interceptors as documentation to set up the desired config, then in VUE

import {onErrorCaptured, ref} from "vue";

setup(){
    let errors = ref(null)
    onErrorCaptured((error)=>{
         // checking for server response first
         errors.value = error.response?Object.values(error.response.data)[0]:error.message
    })
    return{errors}
}

Upvotes: 1

Alex Povolotsky
Alex Povolotsky

Reputation: 402

Add "last load" state into Vuex, and watch for changes in root.

This may look heavy and complicated, but it's logical and separates components well.

Bonus: you'll know instantly if you have loaded your data and was the attempt successful!

Upvotes: 0

fred
fred

Reputation: 1303

I just use the catch. The same thing I was using before I switched to vuex. It's probably the most universal and well documented solution and lets me continue to insert my errors into the html of the components like I was doing before. It also lets me continue to use my loading = true, loading = false html animation.

So I end up with 3 state properties, data, error, and loading. It seems to work for me. Your mileage may vary. I am also using vuex modules and namespacing but here is a simplified example without that

//somevuexstore.js

actions: {

fetchData(context) {

    axios
        .get("api/someendpoint")
        .then(response => {
            context.commit('loading')
            context.commit('organizations', response.data)

        }).catch(error => {
            console.log(error.response.data.message || error.message)
            context.commit('error', error)
        });
},

mutations: {
organizations(state, data) {
    return state.organization = data
},

error(state, data) {
    return state.error = data
},

loading(state) {
    return state.loading = false
},

state= {

organization: [],
error: '',
loading: true
}

Then in my component.vue it's very similar to the way I was doing it before, just with the added computed properties.

computed: {
...mapState({
        getError: 'error',
        getLoading: 'loading',
        getAllOrg: 'organization',
}),
}

mounted() {
      this.$store.dispatch('fetchData')
}

And my html would be stuff like this.

<tr v-for="value in getAllOrg" :key="value.id">
   <td>{{ value.id }}</td>
   <td>{{ value.email }}</td>
   <td>{{ value.name }}</td>
   <td>{{ value.['created-at'] | formatDate }}</td>
</tr>

I insert the error messages where appropriate

<div v-if="getError" class="error">
   <p>{{ getError }}</p>
</div>

For loading animation I use vue spinners package inserted into html where appropriate.

<div v-if="getLoading" style="height:37px;">
    <p>
      <bar-loader class="custom-class" color="#c2c2c2" 
      getLoading="getLoading" 
      :width="130"></bar-loader>
   </p>

Upvotes: 6

Saeed Torabi
Saeed Torabi

Reputation: 41

You can use event bus, like this

import Vue from 'vue'

export const EventBus = new Vue();

and then trigger error

axios.get(...)
  .catch(function (error) {
     EventBus.$emit('error', error)
  });

Upvotes: 1

Phil
Phil

Reputation: 165059

Have your cake and eat it too. Assuming you are already using an interceptor...

axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  store.commit('ERROR', error) // just taking some guesses here
  return Promise.reject(error) // this is the important part
})

This will keep the promise rejection going back to the caller so in your component, something like...

axios.whatever(...).then(res => {
  // happy days
}, err => {
  // oh noes!
})

Upvotes: 27

Leonardo Filipe
Leonardo Filipe

Reputation: 1762

The power of promisses! (plus async/await)

vue method (mycomponent.js)

async YourAsyncMethod() {
    const payload = {key: "var"}
    const result = await axios
        .post('/your/api/endpoint', payload)
        .catch(e => {
            console.log(e.message)
        });
}

yourMethod() {
    // start axios logic
    const payload = {key: "var"}
    axios
        .post('/your/api/endpoint', payload)
        .then(response => {
            console.log(response.data)

            // start state action logic
            this.$store
                .dispatch('yourAction', payload)
                .then(add => {
                    console.log('success mutation!');
                })
                .catch(error => {
                    // error = Error object,
                    console.log('error mutation:',error.message);
                    console.log(error) // to se full error object
                });
        })
        .catch(error => {
            console.log('error axios request', error.data)
        });
}

with state actions (store/actions.js)

yourAction(){
    const some_logic = false;
    if (!some_logic) {
        // when return a Promisse.reject
        //you can get error with catch(e) from youtMethod
        return Promise.reject(new Error("Impressora já adicionada"))
    }
    context.commit('MUTATION_METHOD', payload);
}

with axios

http
    .post('/your/api/endpoint', payload)
    .then(response => {
        console.log(response.data)
    })
    .catch(error => {
        console.log('error', error.data)
    });

Upvotes: 0

Botea Florin
Botea Florin

Reputation: 633

I have come to the conclusion that they can not always exists general methods for handling errors, so they must be somehow coupled to the context. Is a good thing to have separate api files, but mediate this with the mention above. I have separate api files and I am doing the following:

//comments-api.js
export default {
    get (url, handler){
        //return the promise to further possible chains
        return axios.get(url)
            .then( response => handler.success(response.data) )
            .catch( error => handler.serverDownOrUnexpected(error.response) )
    },
}
//comments.js - vuex module
import $comments from './../../services/api/comments-api'
...
actions: {
    $comments.get(url, {
        success: (data) => commit('success_handler', data),
        serverDownOrUnexpected: (error) => commit('unexpected', error)
        //so on...
    })
}
...

in this approch, whenever I want to change the way certain errors are handled, I have to make changes just in one place, plus benefits of decoupled code.

Upvotes: 0

Related Questions