N
Nuxt5d ago
Cordobes

useFetch works perfectly on dev enviroment, on productions fails on pre-hydration

const fetchComments = async () => { try { const { data } = await useFetch('/api/v1/comments', { method: 'GET', params: { sitekey, post_id: postId, hostname: referer }, immediate: true }) comments.value = Array.isArray(data.value) ? data.value : [] } catch (error) { console.error('Error fetching comments:', error) comments.value = [] } } await fetchComments() <div class="space-y-4"> <template v-if="comments.length"> <div v-for="comment in comments" :key="comment.id" class="group"> <div class="flex items-start gap-4"> <UAvatar class="rounded-xl" :src="comment.avatar_url" :ui="{ image: 'rounded-md w-10 h-10' }" /> <div class="flex-1 min-w-0"> <div class="flex items-center gap-2 mb-1"> <UBadge :label="comment.username" variant="subtle" color="primary" class="font-medium" /> </div> <UTooltip :text="formatDate(comment.created_at)"> <span class="text-xs text-gray-500">{{ formatDate(comment.created_at) }}</span> </UTooltip> <p class="text-white break-words">{{ comment.content }}</p> <div class="flex items-center gap-4 mt-2"> <div class="flex items-center gap-1"> <UButton size="xs" color="neutral" variant="ghost" :icon="comment.liked ? 'i-heroicons-hand-thumb-up-solid' : 'i-heroicons-hand-thumb-up'" @click="handleReaction(comment.id, true)" class="hover:text-primary-500" /> :icon="comment.disliked ? 'i-heroicons-hand-thumb-down-solid' : 'i-heroicons-hand-thumb-down'" @click="handleReaction(comment.id, false)" class="hover:text-primary-500" /> <span class="text-xs text-gray-500">{{ comment.dislike }}</span> </div>
No description
No description
17 Replies
kapa.ai
kapa.ai5d ago
Hi @Cordobes. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Cordobes
CordobesOP5d ago
</div> </div> </div> </div> </template> <div v-else class="text-center py-8"> <UIcon name="i-heroicons-chat-bubble-left-right" class="mx-auto h-12 w-12 text-gray-400" /> <h3 class="mt-2 text-sm font-medium text-white">No comments</h3> <p class="mt-1 text-sm text-gray-500">Be the first to comment!</p> </div> </div> Loads perfectly on dev enviroments on production the response of usefetch is working properly but comments render and after hydration it unrenders
kapa.ai
kapa.ai5d ago
---------------------- I can see you're experiencing an issue with useFetch working in development but failing in production. This is a known issue in Nuxt 3.16.0 that several users have reported. The Issue Based on the knowledge sources, there's a specific problem with useFetch in Nuxt 3.16.0 where: 1. The data fetching works correctly in development mode 2. In production, after hydration, the data becomes null 3. This causes hydration mismatches and can lead to errors like "Cannot read properties of null" The root cause appears to be related to how keys are generated for the payload cache. The key calculated on the client side doesn't match the key calculated on the server side, causing a new request to be made instead of using the cached data from the payload. Solutions There are several workarounds you can try: 1. Use a custom key Add a manual key to your useFetch call:
const { data } = await useFetch('/api/v1/comments', {
key: 'comments-fetch', // Add this line
method: 'GET',
params: {
sitekey,
post_id: postId,
hostname: referer
},
immediate: true
})
const { data } = await useFetch('/api/v1/comments', {
key: 'comments-fetch', // Add this line
method: 'GET',
params: {
sitekey,
post_id: postId,
hostname: referer
},
immediate: true
})
2. Switch to useAsyncData + $fetch As mentioned in the knowledge sources, this combination works in both production and development:
const fetchComments = async () => {
try {
const { data } = await useAsyncData(
'comments',
() => $fetch('/api/v1/comments', {
method: 'GET',
params: {
sitekey,
post_id: postId,
hostname: referer
}
})
)
comments.value = Array.isArray(data.value) ? data.value : []
} catch (error) {
console.error('Error fetching comments:', error)
comments.value = []
}
}
await fetchComments()
const fetchComments = async () => {
try {
const { data } = await useAsyncData(
'comments',
() => $fetch('/api/v1/comments', {
method: 'GET',
params: {
sitekey,
post_id: postId,
hostname: referer
}
})
)
comments.value = Array.isArray(data.value) ? data.value : []
} catch (error) {
console.error('Error fetching comments:', error)
comments.value = []
}
}
await fetchComments()
3. Update to Nuxt 3.16.1 or later This issue has been addressed in newer versions of Nuxt. The problem was identified in multiple GitHub issues (#31340, #31355) and has been fixed in subsequent releases. Additional Notes If you're using shamefully-hoist=true in your .npmrc file, this might be contributing to the issue as mentioned in one of the GitHub issues. You might want to try setting it to false if the above solutions don't work. The issue specifically affects useFetch but not useAsyncData, which is why the second solution works as a reliable workaround. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5d ago
<@1275808916168511508> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Cordobes
CordobesOP5d ago
@kapa.ai i want to do cross-site login trought a iframe dialoge.pro is the main site where people log in cordobes.fun has <iframe src="https://dialoge.pro/embed/macro" class="w-full h-96"></iframe> how i can log in trought that iframe
kapa.ai
kapa.ai5d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'll help you understand how to implement cross-site authentication through an iframe in Nuxt. Cross-Site Authentication Challenges When implementing authentication across different domains using iframes, you'll face several challenges related to browser security policies, particularly CORS (Cross-Origin Resource Sharing) and cookie restrictions. From the knowledge sources, there are a few important considerations: 1. CORS Restrictions: Browsers enforce same-origin policy which restricts how documents or scripts from one origin can interact with resources from another origin Stack Overflow Post. 2. Cookie Limitations: Cross-site cookies require specific configurations to work properly. Implementing Cross-Site Authentication in Nuxt Here's how you can approach this with Nuxt: 1. Configure Cookie Settings You'll need to configure your cookies to work across domains:
// In your authentication logic
const authCookie = useCookie('auth-token', {
sameSite: 'none', // Required for cross-site cookies
secure: true, // Required when sameSite is 'none'
domain: '.dialoge.pro' // Use a domain that can be shared
})
// In your authentication logic
const authCookie = useCookie('auth-token', {
sameSite: 'none', // Required for cross-site cookies
secure: true, // Required when sameSite is 'none'
domain: '.dialoge.pro' // Use a domain that can be shared
})
The sameSite attribute is crucial here - setting it to 'none' allows the cookie to be sent in cross-site requests Docs > Api > Composables > useCookie. 2. Enable Cross-Origin Prefetching In your nuxt.config.ts, enable cross-origin prefetching:
export default defineNuxtConfig({
experimental: {
crossOriginPrefetch: true,
},
})
export default defineNuxtConfig({
experimental: {
crossOriginPrefetch: true,
},
})
This will help with prefetching resources across origins Docs > Api > Components > <NuxtLink>. 3. Server-Side Proxy Approach Since browser-side CORS restrictions can be challenging to overcome, a common solution is to proxy the authentication requests through your server:
// server/api/auth/proxy.js
export default defineEventHandler(async (event) => {
// Forward authentication request to dialoge.pro
const response = await fetch('<https://dialoge.pro/api/auth',> {
method: 'POST',
body: await readBody(event),
headers: {
'Content-Type': 'application/json'
}
})

// Return the response
return await response.json()
})
// server/api/auth/proxy.js
export default defineEventHandler(async (event) => {
// Forward authentication request to dialoge.pro
const response = await fetch('<https://dialoge.pro/api/auth',> {
method: 'POST',
body: await readBody(event),
headers: {
'Content-Type': 'application/json'
}
})

// Return the response
return await response.json()
})
Then in your iframe content, use this proxy endpoint instead of directly calling the other domain. 4. Message Passing Between Frames You can use the postMessage API to communicate between your iframe and the parent window:
// In your iframe (dialoge.pro/embed/macro)
function sendLoginStatus(status, userData) {
window.parent.postMessage({
type: 'auth',
status,
userData
}, '<https://cordobes.fun'>)
}

// After successful login
sendLoginStatus('success', { user: 'example' })
// In your iframe (dialoge.pro/embed/macro)
function sendLoginStatus(status, userData) {
window.parent.postMessage({
type: 'auth',
status,
userData
}, '<https://cordobes.fun'>)
}

// After successful login
sendLoginStatus('success', { user: 'example' })
Then in the parent page (cordobes.fun):
// Listen for messages from the iframe
window.addEventListener('message', (event) => {
// Verify the origin for security
if (event.origin !== '<https://dialoge.pro'>) return

if (event.data.type === 'auth' && event.data.status === 'success') {
// Handle successful login
console.log('User logged in:', event.data.userData)
}
})
// Listen for messages from the iframe
window.addEventListener('message', (event) => {
// Verify the origin for security
if (event.origin !== '<https://dialoge.pro'>) return

if (event.data.type === 'auth' && event.data.status === 'success') {
// Handle successful login
console.log('User logged in:', event.data.userData)
}
})
Important Security Considerations 1. Always validate the origin of messages in your message event listeners 2. Use HTTPS for both domains 3. Consider implementing additional security measures like token validation Remember that cross-site authentication has inherent security challenges, and you should carefully evaluate whether this approach meets your security requirements. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5d ago
<@1275808916168511508> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Cordobes
CordobesOP5d ago
@kapa.ai is there a better way?
kapa.ai
kapa.ai5d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on your previous question about cross-site login through an iframe, there are indeed better approaches to handle authentication across different domains in a Nuxt application. Better Approaches for Cross-Domain Authentication 1. Use a dedicated authentication module Nuxt Auth Utils is an official module designed specifically for managing user sessions in Nuxt applications. This provides a more structured and secure approach than manually implementing cross-site authentication.
// Install the module
// npm install nuxt-auth-utils

// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-auth-utils']
})
// Install the module
// npm install nuxt-auth-utils

// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-auth-utils']
})
2. Server-side authentication with tokens Instead of relying on cookies (which have cross-domain limitations), implement a token-based authentication system: 1. The user logs in on dialoge.pro 2. The server generates a JWT token 3. This token is passed to cordobes.fun via URL parameters or postMessage 4. cordobes.fun validates the token on its server side This approach avoids many of the cookie-related cross-domain issues. 3. Use a centralized authentication service Implement a single sign-on (SSO) solution where both domains authenticate against a central service. Popular options that work well with Nuxt include: - Auth0 - Keycloak - Firebase Authentication For example, with Firebase and Nuxt VueFire:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-vuefire', '@vueuse/nuxt'],
vuefire: {
auth: true,
config: {
// your firebase config
}
}
})
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-vuefire', '@vueuse/nuxt'],
vuefire: {
auth: true,
config: {
// your firebase config
}
}
})
4. OAuth flow with redirect Instead of using an iframe (which has many security restrictions), implement a standard OAuth flow: 1. Redirect the user from cordobes.fun to dialoge.pro/login 2. After authentication, redirect back to cordobes.fun with an auth token 3. Validate the token on your server This is more secure and reliable than iframe-based approaches. Security Considerations The approaches above are generally more secure than iframe-based authentication because: 1. They avoid cross-site cookie issues 2. They don't rely on postMessage which can be vulnerable to certain attacks 3. They follow established authentication patterns with better security properties Remember that any cross-domain authentication solution needs careful security review to prevent vulnerabilities like CSRF attacks. Would you like more specific details on implementing any of these approaches? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5d ago
<@1275808916168511508> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Cordobes
CordobesOP5d ago
@kapa.ai
kapa.ai
kapa.ai5d ago
Let me check the knowledge base for you again. One moment! :books:
Cordobes
CordobesOP5d ago
i am using supabase auth
kapa.ai
kapa.ai5d ago
Sorry @Cordobes, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Cordobes
CordobesOP5d ago
@kapa.ai i am using supabase auth i want to do cross-site login trought a iframe dialoge.pro is the main site where people log in cordobes.fun has <iframe src="https://dialoge.pro/embed/macro" class="w-full h-96"></iframe> how i can log in trought that iframe
kapa.ai
kapa.ai5d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I see you're using Supabase Auth with Nuxt and want to implement cross-site login through an iframe. Let me provide some specific guidance based on Nuxt and Supabase. Supabase Auth in Nuxt with iframes Based on the knowledge sources, here are some approaches to handle this scenario: 1. Create a callback page for OAuth From the knowledge sources, we can see that Supabase OAuth requires a callback page:
// Create a confirm.vue page in your pages directory
// This is required for Supabase OAuth to work properly
<template>
<div>Processing authentication...</div>
</template>

<script setup>
// This page will handle the OAuth callback
</script>
// Create a confirm.vue page in your pages directory
// This is required for Supabase OAuth to work properly
<template>
<div>Processing authentication...</div>
</template>

<script setup>
// This page will handle the OAuth callback
</script>
As mentioned in this Stack Overflow post, creating a confirm.vue page is essential for Supabase OAuth to work correctly. 2. Configure Supabase redirects in nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
supabase: {
url: process.env.SUPABASE_URL,
key: process.env.SUPABASE_KEY,
redirectOptions: {
login: '/login',
callback: '/confirm',
exclude: [], // Don't exclude paths that need authentication
},
},
})
// nuxt.config.ts
export default defineNuxtConfig({
supabase: {
url: process.env.SUPABASE_URL,
key: process.env.SUPABASE_KEY,
redirectOptions: {
login: '/login',
callback: '/confirm',
exclude: [], // Don't exclude paths that need authentication
},
},
})
3. Implement the login functionality In your iframe content (on dialoge.pro/embed/macro), implement the login function:
<template>
<div>
<button @click="login">Log in with GitHub</button>
<!-- Add other providers as needed -->
</div>
</template>

<script setup lang="ts">
const supabase = useSupabaseClient();
const login = async () => {
// Get the parent URL to redirect back to
const parentOrigin = document.referrer || '<https://cordobes.fun';>

const { error } = await supabase.auth.signInWithOAuth({
provider: 'github', // or any other provider
options: {
redirectTo: `${window.location.origin}/confirm?redirect=${encodeURIComponent(parentOrigin)}`
},
});

if (error) {
console.error('Login error:', error);
}
};
</script>
<template>
<div>
<button @click="login">Log in with GitHub</button>
<!-- Add other providers as needed -->
</div>
</template>

<script setup lang="ts">
const supabase = useSupabaseClient();
const login = async () => {
// Get the parent URL to redirect back to
const parentOrigin = document.referrer || '<https://cordobes.fun';>

const { error } = await supabase.auth.signInWithOAuth({
provider: 'github', // or any other provider
options: {
redirectTo: `${window.location.origin}/confirm?redirect=${encodeURIComponent(parentOrigin)}`
},
});

if (error) {
console.error('Login error:', error);
}
};
</script>
4. Handle post-authentication communication After authentication is complete, you'll need to communicate the auth state back to the parent window:
// In your confirm.vue page
<script setup>
const route = useRoute();
const supabase = useSupabaseClient();
const user = useSupabaseUser();

// Watch for user authentication
watchEffect(() => {
if (user.value) {
// Get the redirect URL from query params
const redirectUrl = route.query.redirect as string;

if (redirectUrl) {
// Send message to parent window
window.opener?.postMessage({
type: 'SUPABASE_AUTH_COMPLETE',
user: user.value
}, new URL(redirectUrl).origin);

// Close popup if opened as one
if (window.opener) {
window.close();
}
}
}
});
</script>
// In your confirm.vue page
<script setup>
const route = useRoute();
const supabase = useSupabaseClient();
const user = useSupabaseUser();

// Watch for user authentication
watchEffect(() => {
if (user.value) {
// Get the redirect URL from query params
const redirectUrl = route.query.redirect as string;

if (redirectUrl) {
// Send message to parent window
window.opener?.postMessage({
type: 'SUPABASE_AUTH_COMPLETE',
user: user.value
}, new URL(redirectUrl).origin);

// Close popup if opened as one
if (window.opener) {
window.close();
}
}
}
});
</script>
5. Listen for auth events on the parent site On cordobes.fun, add a listener for the authentication events:
<script setup>
// Listen for messages from the iframe
window.addEventListener('message', (event) => {
// Verify the origin
if (event.origin !== '<https://dialoge.pro'>) return;

if (event.data.type === 'SUPABASE_AUTH_COMPLETE') {
// Handle successful login
console.log('User logged in:', event.data.user);
// Refresh the page or update UI as needed
}
});
</script>
<script setup>
// Listen for messages from the iframe
window.addEventListener('message', (event) => {
// Verify the origin
if (event.origin !== '<https://dialoge.pro'>) return;

if (event.data.type === 'SUPABASE_AUTH_COMPLETE') {
// Handle successful login
console.log('User logged in:', event.data.user);
// Refresh the page or update UI as needed
}
});
</script>
Important considerations 1. Supabase redirects: By default, Supabase redirects to the login page for unauthenticated users trying to access protected routes, as mentioned in this Stack Overflow post. 2. Cross-domain cookies: Supabase uses cookies for session management, which can be problematic across domains. You might need to use token-based authentication instead. 3. Security: Be cautious with cross-domain authentication as it introduces security risks. Always validate origins and use HTTPS. Would you like more specific details on any part of this implementation? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5d ago
<@1275808916168511508> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?