vue.jsaxiosvuejs3vue-routervue-router4

Vue 3 Vue-router 4 Composition API - Calling router.push outside of component not working


In my Vue 3 project, i use the composition API of Vue and i have axios defined as a plugin in plugins/axios.js like this:

import axios from "axios";
import { useRouter } from "vue-router";
import { useErrorStore } from "../stores/error";

axios.interceptors.response.use(
    (response) => {
      return response;
    },
    (error) => {
      const errorStore = useErrorStore();
      errorStore.state.errors.push(error.response.data.detail);
      if (parseInt(error.response.status) == 422) {
        const router = useRouter();
        router.push({
          name: "login",
        });
      }
      return Promise.reject(error);
    }
  );
export default axios;

ANTICIPATED BEHAVIOR: When the API call returns a 422 status, meaning the access token is invalid/ expired, the user shall be redirected to the login page.

PROBLEM: router.push does not work, i am not redirected when the error occurs. And i am not getting any error message on the console either.

On the other hand, the errorStore as defined and accessed in the error case works just fine.

QUESTION: How can i redirect the user to another route from within the custom axios plugin file? Alternatively, is there a better/ more common way to achieve this?

Thanks for your help!


Solution

  • I'm going to answer this since it keeps coming up in Google searches and @Estus Flask's comments are correct.

    You shouldn't use composables outside of script setup tags. But in this case, a composable isn't necessary, as the router can be imported directly and still used as expected. So instead of...

    import { useRouter } from 'vue-router';
    const router = useRouter();
    

    Directly import the router object that you have set up for your application startup. So, in @/router/index.js, say you have something like this:

    import { createRouter, createWebHistory } from 'vue-router';
    
    const router = createRouter({
        history: createWebHistory(import.meta.env.BASE_URL),
        routes: [
            // your routes here
        ]
    });
    
    export default router;
    

    Then, in your Axios plugin, you would have something like this:

    import axios from 'axios';
    import router from '@/router';
    
    axios.interceptors.response.use(
        (response) => {
            return response;
        },
        (error) => {
            // whatever else you want to do with the error
            router.push({ name: 'login' });
        }
    );
    

    From my understanding, the router is a singleton and you will be using the constructed/live instance.