When using a composable which uses useRoute
inside middleware I receive this warn: '[nuxt] Calling useRoute within middleware may lead to misleading results. Instead, use the (to, from) arguments passed to the middleware to access the new and old routes.'
I tried the solutions from here: https://github.com/nuxt-modules/i18n/issues/2064 and here Nuxt i18n Calling useRoute within middleware may lead to misleading results, but they didn’t work for me. I’m trying to understand whether this is a bug that I should wait to be fixed or if there’s an actual solution. I’m using useRoute
inside useStoreAuth()
, but not inside checkSession
. Since I added useStoreAuth
to the middleware, it says I can’t use useRoute
. Creating a separate composable for the signIn
function where I use useRoute
won’t work, because I need to access the signedIn
ref from useStoreAuth
in both checkSession
and signIn
functions. Besides, I need to use checkSession
within signIn
, which means if I create a separate composable called useStoreSession
and add it to the middleware, it will also contain useStoreAuth
with useRoute
.
export default defineNuxtRouteMiddleware(async (to, _from) => {
const { signedIn, checkSession } = useStoreAuth();
await checkSession();
if (!signedIn.value && to.path.startsWith('/profile')) {
return navigateTo(`/sign-in?redirect=${encodeURIComponent(to.path)}`);
}
if (signedIn.value && to.path === '/sign-in') {
return navigateTo('/');
}
});
The way I use useRoute
inside a composable:
export const useStoreAuth = () => {
const route = useRoute();
const signIn = async (signInData: SignInData) => {
const { apiBase } = useRuntimeConfig().public;
state.pending = true;
state.error = null;
state.successMessage = null;
const res = await fetch(`${apiBase}/signIn`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(signInData),
});
try {
if (res.ok) {
await checkSession();
if (state.signedIn) {
if (route.query.redirect) {
await navigateTo(`${route.query.redirect}`);
} else {
await navigateTo(`${route.query.redirect}` || '/');
}
state.successMessage = 'Signed in successfully';
} else {
state.error = 'Error signing in';
}
} else if (res.status === 400) {
state.error = 'Validation error';
} else {
const resData = await res.json();
state.error = resData.message || "User doesn't exist";
}
} catch (err) {
state.error = `An unexpected error occurred ${err.message}`;
} finally {
state.pending = false;
}
};
return {
...toRefs(state),
signIn,
};
};
Looking for a possible solution.
In order to reliably use composables in Pinia store it should be guaranteed to be initialized at the time when they are allowed, i.e. the first useStore()
call should happen in component's setup, not earlier. This is not feasible if a store is used in other places such as router hooks, the first navigation can occur in Vue before component instance is created. In case of Nuxt's useRoute
the warning explain the problem; even if it's allowed, the value is ambiguous during the navigation.
A more practical approach is to inject a store with composable result at the moment when a composable is accessible, e.g. in root component:
store.route = useRoute()
In this case this is the solution because only checkSession
is used in router hook. As long as it doesn't use route
, it doesn't matter if the latter is not initialized at this point. This would be a problem if route
is used prior to this, and a possible workaround would be to allow a method that uses it (signIn
) to optionally accept route object via a parameter. So it could be used as store.signIn(data, to)
in router hook, and signIn
could use it as (route.value ?? routeArg).query.redirect
. Again, not a problem here, but that's how it would be approached if it were.
This also indicates a possible design problem, a store contains routing logic that could belong to a router. In this case sign-in
route and router hooks could handle redirect
parameter and possible redirects, and root component could react to the changes in store.signedIn
state, while the store would be responsible for the logic that isn't tied to specific route implementation.