N
Nuxt3mo ago
Paul

useCookie not being reactive

Hello, I'm currently trying to implement login function and I want to store the access token in the cookie. Here's the following setup: composables/useAuth.ts: const useAuth = () => { const accessToken = useCookie('at'); const login = async () => { const data = await $fetch('/auth/login', { method: 'post', body: param }) accessToken.value = data.access_token }; return { login }; }; pages/login.vue: <script lang="ts" setup> const {login} = useAuth() const form = ref({ email: '', password: '' }) const accessToken = useCookie('at') const onSubmit = async () => { try { await login(form.value) console.log(accessToken.value) <-- this returns undefined } catch (e) { } } </script> From the above, when I called accessToken.value after the login method, it's still undefined, although it returns actual token after I refresh the page fully. Why is that? How can I fix it? Thank you!
11 Replies
kapa.ai
kapa.ai3mo ago
Hi @Paul. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Thank you for your question about the useCookie composable in Nuxt. I understand you're experiencing an issue with the reactivity of the cookie value after setting it in a login function. Let's address this problem based on the information provided in the knowledge sources. The issue you're experiencing is likely related to how cookies are handled on the server side and how useCookie updates its value. Here are a few points to consider: 1. Server-side cookie handling: When you set a cookie using useCookie on the server side (which happens during your login function), the cookie value is not immediately accessible in the same request cycle. This is because useCookie gets its value from event.node.req.headers.cookie, which is not updated until the next request GitHub PR #21940 (https://github.com/nuxt/nuxt/pull/21940). 2. Manual refresh: To address this issue, Nuxt introduced the refreshCookie utility in version 3.10. This function allows you to manually refresh the useCookie value when you know a new cookie has been set in the browser refreshCookie docs (https://nuxt.com/docs/api/utils/refresh-cookie). To fix your issue, you can modify your login function in the useAuth composable like this:
import { refreshCookie } from '#app'

const useAuth = () => {
const accessToken = useCookie('at');

const login = async (param) => {
const data = await $fetch('/auth/login', {
method: 'post',
body: param
})
accessToken.value = data.access_token
refreshCookie('at')
};

return { login };
};
import { refreshCookie } from '#app'

const useAuth = () => {
const accessToken = useCookie('at');

const login = async (param) => {
const data = await $fetch('/auth/login', {
method: 'post',
body: param
})
accessToken.value = data.access_token
refreshCookie('at')
};

return { login };
};
By calling refreshCookie('at') after setting the cookie value, you ensure that the useCookie ref is updated with the new value immediately, without waiting for the next request. Additionally, you might want to consider enabling the experimental cookieStore feature, which automatically refreshes useCookie values when cookies change in the browser Experimental Features docs (https://nuxt.com/docs/guide/going-further/experimental-features#cookiestore):
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
cookieStore: true
}
})
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
cookieStore: true
}
})
By implementing these changes, the accessToken.value in your onSubmit function should correctly reflect the updated value immediately after the login call. Remember to always use the latest version of Nuxt to benefit from bug fixes and improvements related to cookie handling. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@524551031133044737> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Paul
PaulOP3mo ago
I used the refreshCookie inside the useAuth composable, and now I added fetchUser inside the composable: import { refreshCookie } from '#app' const useAuth = () => { const accessToken = useCookie('at'); const login = async (param) => { const data = await $fetch('/auth/login', { method: 'post', body: param }) accessToken.value = data.access_token refreshCookie('at') await fetchUser() }; async function fetchUser() { try { const response = await $api('/auth/user', {}); user.value = response.data; } catch (error) { console.error('Failed to fetch user data:', error); logout(); } } return { login }; }; And I implemented a custom wrapper for $fetch as a plugin: export default defineNuxtPlugin((nuxtApp) => { const config = useRuntimeConfig() const baseUrl = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl const accessTokenCookie = useCookie('at') const api = $fetch.create({ baseURL: baseUrl, headers: { Accept: 'application/json' }, onRequest({ options }) { if (accessTokenCookie.value && !options.headers?.Authorization) { options.headers = { ...options.headers, Authorization: Bearer ${accessTokenCookie.value}, } } }, }) return { provide: { api } } }) However, fetchUser() fails as accessTokenCookie.value is still undefined after the refreshCookie(). How do I fix it? @kapa.ai
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Thank you for providing more details about your implementation. I understand you're now facing an issue where fetchUser() fails because accessTokenCookie.value is still undefined after calling refreshCookie(). Let's analyze this problem based on the information available in our knowledge sources. The issue you're experiencing is likely related to the timing of when the cookie value becomes available across different parts of your application. Here are a few points to consider: 1. Cookie Refresh Timing: While refreshCookie() updates the useCookie ref, this update might not be immediately reflected in other parts of your application, especially in plugins that run early in the Nuxt lifecycle refreshCookie docs (https://nuxt.com/docs/api/utils/refresh-cookie). 2. Server-Side vs. Client-Side Behavior: The behavior of cookies can differ between server-side and client-side rendering, which might affect how quickly the updated cookie value is available Passing Headers and Cookies (https://nuxt.com/docs/getting-started/data-fetching#passing-headers-and-cookies). To address this issue, you can try the following approaches: 1. Use useRequestHeaders in your API plugin: Instead of directly accessing the cookie in the plugin, you can use useRequestHeaders to get the most up-to-date cookie value. Modify your plugin like this:
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const baseUrl = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl

const api = $fetch.create({
baseURL: baseUrl,
headers: {
Accept: 'application/json'
},
onRequest({ options }) {
const headers = useRequestHeaders(['cookie'])
const cookies = parseCookies(headers.cookie || '')
const accessToken = cookies.at
if (accessToken && !options.headers?.Authorization) {
options.headers = {
...options.headers,
Authorization: `Bearer ${accessToken}`,
}
}
},
})

return {
provide: {
api
}
}
})

export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const baseUrl = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl

const api = $fetch.create({
baseURL: baseUrl,
headers: {
Accept: 'application/json'
},
onRequest({ options }) {
const headers = useRequestHeaders(['cookie'])
const cookies = parseCookies(headers.cookie || '')
const accessToken = cookies.at
if (accessToken && !options.headers?.Authorization) {
options.headers = {
...options.headers,
Authorization: `Bearer ${accessToken}`,
}
}
},
})

return {
provide: {
api
}
}
})

You'll need to implement a parseCookies function to extract the at cookie value from the cookie string. 2. Move the token handling to the fetchUser function: Instead of relying on the plugin to set the Authorization header, you can set it directly in the fetchUser function:
async function fetchUser() {
try {
const { $api } = useNuxtApp()
const accessToken = useCookie('at')
const response = await $api('/auth/user', {
headers: {
Authorization: `Bearer ${accessToken.value}`
}
});
user.value = response.data;
} catch (error) {
console.error('Failed to fetch user data:', error);
logout();
}
}

async function fetchUser() {
try {
const { $api } = useNuxtApp()
const accessToken = useCookie('at')
const response = await $api('/auth/user', {
headers: {
Authorization: `Bearer ${accessToken.value}`
}
});
user.value = response.data;
} catch (error) {
console.error('Failed to fetch user data:', error);
logout();
}
}

3. Use a slight delay before calling fetchUser: As a last resort, you can introduce a small delay to ensure the cookie value has been updated:
const login = async (param) => {
const data = await $fetch('/auth/login', {
method: 'post',
body: param
})
accessToken.value = data.access_token
refreshCookie('at')

// Add a small delay
await new Promise(resolve => setTimeout(resolve, 100))

await fetchUser()
};

const login = async (param) => {
const data = await $fetch('/auth/login', {
method: 'post',
body: param
})
accessToken.value = data.access_token
refreshCookie('at')

// Add a small delay
await new Promise(resolve => setTimeout(resolve, 100))

await fetchUser()
};

Note that this is not an ideal solution and should be used cautiously. Remember that the exact behavior can depend on various factors, including how your Nuxt application is configured and deployed. If these solutions don't resolve the issue, you might need to review your overall authentication flow and consider using a more robust state management solution for handling user authentication state. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@524551031133044737> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Paul
PaulOP3mo ago
Hmm. The 2nd approach works though. Still kinda weird for me how the cookie inside the custom plugin doesn't update accordingly. @kapa.ai
Perhaps someone in the community could help (?)
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'm glad to hear that the second approach worked for you. You're right that it's a bit unusual for the cookie in the custom plugin not to update immediately. This behavior is indeed somewhat complex and can be confusing. Regarding your suggestion about community help, there are a couple of relevant points from our knowledge sources: 1. There has been ongoing discussion and development around cookie handling in Nuxt. For instance, there was a recent fix for issues related to cookie reactivity GitHub PR #25255 (https://github.com/nuxt/nuxt/pull/25255). 2. The Nuxt team has been working on improving cookie handling, including the introduction of the refreshCookie utility and the experimental cookieStore feature Experimental Features docs (https://nuxt.com/docs/api/nuxt-config#cookiestore). If you're still curious about the underlying reasons for this behavior or want to explore more advanced solutions, you have a few options: 1. You could post a question on the Nuxt GitHub repository as an issue. This would allow you to directly interact with the Nuxt maintainers and other community members who might have encountered similar situations. 2. You could also ask for help on the Nuxt Discord channel or the Nuxt section of the Vue.js Forum, where many experienced Nuxt developers gather to discuss and solve problems. 3. If you suspect this might be a bug or an area for improvement in Nuxt, you could consider opening a discussion on the Nuxt GitHub repository to get insights from the core team and the community. Remember to provide a minimal reproducible example when seeking help, as it will make it easier for others to understand and assist with your specific use case. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@524551031133044737> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Paul
PaulOP3mo ago
Anyway, any step to implement retry onResponseError with $fetch? @kapa.ai `$fetch.create({ onResponseError: async () => { await refreshToken() //retry the request } })
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! I can help you with implementing a retry mechanism for onResponseError with $fetch in Nuxt. Based on the information from our knowledge sources, here's how you can implement this:
const api = $fetch.create({
baseURL: config.public.apiBaseUrl,
retry: 1,
onRequest({ options }) {
// Add your authentication logic here
},
async onResponseError({ request, response, options }) {
if (response.status === 401) {
// Refresh the token
await refreshToken()

// Retry the original request
return $fetch(request, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${getNewToken()}` // Implement getNewToken() to retrieve the new token
}
})
}
}
})
const api = $fetch.create({
baseURL: config.public.apiBaseUrl,
retry: 1,
onRequest({ options }) {
// Add your authentication logic here
},
async onResponseError({ request, response, options }) {
if (response.status === 401) {
// Refresh the token
await refreshToken()

// Retry the original request
return $fetch(request, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${getNewToken()}` // Implement getNewToken() to retrieve the new token
}
})
}
}
})
This implementation is based on the custom $fetch example from the Nuxt documentation on custom useFetch (https://nuxt.com/docs/guide/recipes/custom-usefetch#custom-fetch). Here's what's happening: 1. We set retry: 1 to allow one retry attempt. 2. In the onResponseError handler, we check if the response status is 401 (Unauthorized). 3. If it is, we call a refreshToken() function (which you need to implement) to get a new token. 4. After refreshing the token, we retry the original request using $fetch, passing the original request and options, but updating the Authorization header with the new token. You'll need to implement the refreshToken() and getNewToken() functions according to your authentication logic. Remember to handle potential errors in the token refresh process and implement proper error handling for cases where the retry might fail. This approach should allow you to automatically retry failed requests due to authentication issues, improving the user experience in your Nuxt application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@524551031133044737> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?