menu

Questions & Answers

How to call useRouter inside useFetch's onFetchError by VueUse?

I need to create a custom fetch composable from VueUse using createFetch() and I want to check if a request returns 401 status, I'd like the route to be redirected to the login route.

export const useApiFetch = createFetch({
  baseUrl: import.meta.env.VITE_API_BASE_URL,
  options: {
    beforeFetch({ options }) {
      const { user } = useSessionStore()
      if (user)
        options.headers.Authorization = `Basic ${user.user_id}:${user.password}`
      return { options }
    },
    onFetchError(response) {
      const route = useRoute()
      const router = useRouter()
      if (route.name !== 'login' && response.status === 401)
        return router.push('/login')
    }
  }
})

But everytime it hits the error, useRoute and useRouter are undefined, and yes.. I have checked that it runs in setup

<script setup>
const submit = async () => {
  const { error, data } = await useApiFetch('/login').post(form)
}
</script>

Did I miss something or is there a better way to do this? thanks

Answers(1) :

Vue composables are primarily expected to be called in setup block. Other usages depend on their implementation and needs to be confirmed. The main restriction is that a composable is linked to specific component instance, in this case useRouter uses provide/inject to get router instance through component hierarchy; this often can be be deduced without checking the actual implementation.

It's possible to directly import router instance instead of using useRouter but this may result in module circular dependencies and this may not be work for other composables.

createFetch wasn't designed for this usage and needs to be wrapped with custom composable that guarantees that other composables will be called in time:

let useApiFetchFn;

const useApiFetch = (...args) => {
  if (!useApiFetchFn) {
    const sessionStore = useSessionStore()
    const route = useRoute()
    const router = useRouter()

    useApiFetchFn = createFetch(...)
  }

  return useApiFetchFn(...args);
}

Whether it's correct to cache the result to useApiFetchFn depends on the implementation, in this case it's acceptable. At this point it may be more straightforward to use useFetch directly and compose the options similarly to how createFetch does that, most of its code is dedicated to TS support and variadic arguments that may not be needed in this case.