Skip to content

Wait for axios API call in vuex to finish before continuing vue-router guard

I have a django-rest-axios-vuejs application stack for which I’m trying to do something in the vue-router.

In the vue-router beforeEach guard, I’m verifying permissions and it is done by checking something in an object called me in vuex store. Everything works well except when I refresh the page.

Indeed refreshing the page also clears vuex store and my beforeEach tries to check the me object from the store which is empty.

Therefore I’d like to fetch this me object from the API if it isn’t in the store. The problem is that it takes “some time” and the hasPermission() method executes before the API call is finished.

So I tried to put a await keyword before my API call but it doesn’t work.

My beforeEach guard :

router.beforeEach(async (to, from, next) => {
  const isLoggedIn = getIsLoggedIn()
  handleLoggedInStatus(isLoggedIn)
  if (to.meta.requiresAuth) {
    if (isLoggedIn) {
      if (to.meta.permission) {
        if (!store.state.me) await store.dispatch('FETCH_ME')
        hasPermission(to.meta.permission) ? next() : next({ name: 'HomePage' })
      } else {
        next()
      }
    } else {
      next({ name: 'LoginForm' })
    }
  } else {
    next()
  }
})

My action from the store :

actions: {
    FETCH_ME: (state) => {
      http
        .get('base/users/me/')
        .then(response => {
          state.me = response.data
        })
        .catch(error => {
          console.log(error)
        })
    }
  }

The only way I’ve found to make it wait is to do the following :

function sleep (ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

router.beforeEach(async (to, from, next) => {
  const isLoggedIn = getIsLoggedIn()
  handleLoggedInStatus(isLoggedIn)
  if (to.meta.requiresAuth) {
    if (isLoggedIn) {
      if (to.meta.permission) {
        if (!store.state.me) {
          store.dispatch('FETCH_ME')
          await sleep(2000)
        }
        hasPermission(to.meta.permission) ? next() : next({ name: 'HomePage' })
      } else {
        next()
      }
    } else {
      next({ name: 'LoginForm' })
    }
  } else {
    next()
  }
})

To make it wait a “random” (2 seconds) amount of time with a little sleep() method.

I’m kinda new to the usage of async await so.. What am I missing to make await store.dispatch('FETCH_ME') work ?

Thanks in advance 🙂

Answer

I finally found this LINK which I didn’t see before…

This allowed me to rewrite my FETCH_ME action like this :

FETCH_ME ({ commit }) {
  return new Promise((resolve, reject) => {
    http
     .get('base/users/me/')
     .then(response => {
       commit('SET_ME', response.data)
       resolve()
     })
     .catch(error => {
       console.log(error)
       reject(error)
     })
   })
}

where SET_ME is a mutation I already had :

SET_ME: (state, user) => {
  state.me = user
},

Which is finally working for my case, where doing this in the router.beforeEach guard :

if (!store.state.me) await store.dispatch('FETCH_ME') effectively waits for the dispatch action to finish.