Home > Software engineering >  How to initialize the Vue app depending on back-end response first?
How to initialize the Vue app depending on back-end response first?

Time:01-02

I want some code to be ran before anything else in the app, that code will send a request to the Back-end and then update the store. I need that part to be executed first because route guards depend on it, how to achieve that?

Code Example

Fetching user information & settings

async init() {
    await AuthService.initCSRF();
    await AuthService.getUser().then((res) => {
      if (res.data && res.data.user) {
        this.loginLocally(res.data.user);
      } else {
        this.logoutLocally();
      }
    });
}

Auth guard

export function isLoggedIn(to, from, next) {
  console.log('Checked', store.state.auth.isLoggedIn);
  if (store.state.auth.isLoggedIn) {
    next();
    return;
  }

  next({ name: 'login' })
}

CodePudding user response:

in my old project, I did something like this. hope you get some idea.

app.js

import App from './components/App.vue'
store.dispatch('auth/attempt', sessionStorage.getItem('token')).then(() =>{
new Vue({
    el: '#app',
    router,
    store,
    render: h => h(App),
 });
});

here I'm validating the token saved in local storage before rendering the app.

my Vuex actions something like this

async signIn({dispatch}, credentials) {
  let response = await axios.post("auth/signin", credentials);
  await dispatch('attempt', response.data.token)
},


async attempt({commit, state}, token) {
  if (token) {
    await commit('SET_TOKEN', token);

  }
  if (!state.token) {
    return;
  }

  try {
    let response = await axios.get('auth/me');
    commit('SET_USER', response.data)
  } catch (e) {
    commit('SET_TOKEN', null);
    commit('SET_USER', null);
  }
},
async signOut({commit}) {
  axios.post('auth/signout').then(() => {
    commit('SET_TOKEN', null);
    commit('SET_USER', null);
  });
}

I'm using a subscriber to listen to mutations and add or remove token in request headers

import store from '../store'

store.subscribe((mutation) => {

 if (mutation.type === 'auth/SET_TOKEN') {
  if (mutation.payload) {
   axios.defaults.headers.common['Authorization'] = `Bearer ${mutation.payload}`;
  sessionStorage.setItem('token', mutation.payload);
} else {
  axios.defaults.headers.common['Authorization'] = null;
  sessionStorage.removeItem('token');
  }
 }
});

Finally an axios interceptor for handle token expiration.

import router from '../plugins/router'
import store from '../store'
import axios from "axios";

axios.interceptors.response.use((response) => {

  return response;
}, (error) => {
if (error.response.status) {
  if (error.response.status === 401) {

  if (router.currentRoute.name !== 'landing') {
    store.dispatch('auth/clearToken').then(() => {
      router.replace({
        name: 'landing'
      });

    }).finally(() => {
      swal.fire(
        'Your Session is Expired',
        'Please sign in again!',
        'error'
      )
    });
    }
  }
}

return Promise.reject(error);
});

CodePudding user response:

Initialization promise can be waited before calling .mount(...). The problem is that Vue router starts separately from the application and won't be delayed this way.

If it's the router that depends on initialization process, a way to delay router start is to wait for the promise in router hook that is triggered before other ones:

const initPromise = init();

...

router.beforeEach(async (to, from, next) => {
  await initPromise;
  next();
});
  • Related