Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSR for user page using useAsyncData & useStrapiAuth (or useStrapiUser) #395

Open
kalnode opened this issue Feb 25, 2024 · 0 comments
Open
Labels
question Further information is requested

Comments

@kalnode
Copy link

kalnode commented Feb 25, 2024

I have a working app using Nuxt and this @nuxt/strapi module. On initial-landing, the server successfully renders pages, with content from Strapi, using Nuxt's useAsyncData.

Below is the typical format of my pages:

<script setup lang="ts">
const { findOne } = useStrapi()
// ...
const { data:event, pending }: any = await useAsyncData( 'event', () => {
    return new Promise<Event>(async (resolve) => {
       await findOne<Event>('events', id.value)
       .then( (response) => {
           resolve(response)
       })
       .catch( (error) => {
           console.log(error)
       })
    })
})
// ...
</script>
<template>
    <div>
        <!-- ... -->
        {{ event.Title }}
        <!-- ... -->
    </div>
</template>

Affirming the SSR works, as a test I wrap the await findOne with a set timeout, of say 5 seconds, and indeed the server waits for the timeout before sending the initial rendered payload to client. No hydration reported errors client-side.

Problems

I have problems taking the above format and applying it to my profile page for the logged-in user. FYI, the Nuxt page route is users/me; there is no id param in the page route.

Goals:

  • To have a proper server-side-rendered (SSR'd) page for the logged-in user profile, with minimum Strapi requests
  • Understand why this is happening, so as to avoid it for other cases

Below, instead of findOne, I'm attempting to use other helpers this module provides thinking this is the more effective thing to do. But also, findOne would require a user id and in the context of server-rendering this page, I don't conveniently know the logged-in user's id.

Trial 1 - fetchUser (useStrapiAuth())

<script setup lang="ts">
const { fetchUser } = useStrapiAuth()
// ...
const { data:MyProfile, pending }: any = await useAsyncData( 'user', () => {
    return new Promise<StrapiUser>(async (resolve) => {
        await fetchUser()
        .then( (response) => {
            resolve(response as unknown as StrapiUser)
        })
       .catch( (error) => {
           console.log(error)
       })
    })
})
// ...
</script>
<template>
    <div>
        <!-- ... -->
        {{ MyProfile }} - This object shows up instantly; it's getting SSR'd (passes the set timeout test as per earlier)
        {{ MyProfile.Title }} - This shows up delayed (after client-render) and comes with a hydration warning.
        <!-- ... -->
    </div>
</template>

In doing this, the page content is visible in the client, although it flicks-in after page load, indicating it's not being SSR'd. As well there's a client-side hydrate error:

[Vue warn]: Hydration text mismatch:
- Client: " "
- Server: " JohnSmith " 

After the fact I see this note in docs re: fetchuser:
https://strapi.nuxtjs.org/auth#fetchuser

This method is called on the server-side only and the data are hydrated client-side so the HTTP call happens only once. This method is called by default on init through a Nuxt plugin, so you don't have to.

So maybe this isn't the thing to use? It wasn't clear to me.

Trial 2 - useStrapiUser()

This had the same results as trial 1:

const { data:MyProfile, pending }: any = await useAsyncData( 'user', () => {
    return new Promise<StrapiUser>(async (resolve, reject) => {
        resolve(await useStrapiUser() as unknown as StrapiUser)
    })
})

Trial 3 - Hydration error solved, but no SSR:

Wrapping the specific offending property with Nuxt's <ClientOnly> clears the hydration warning. This page will be very long, possibly, and essentially I'd need to wrap the entire page with this. This doesn't satisfy SSR at all.

        <ClientOnly>
            {{ MyProfile.Title }}
        </ClientOnly>

Trial 4 - Everything works, but multiple requests occur

Here I first get the user with fetchUser(), simply to be able to get the logged-in user id.
With this id, I run a regular findOne request to Strapi. All together, this satisfies SSR, and has no hydration warnings. To affirm, it also passed the set timeout test. However I'm concerned too many requests are happening, and what I get in fetchUser is essentially the same data as from the findOne (silly).

const { data:MyProfile, pending }: any = await useAsyncData( 'user', () => {
    return new Promise<StrapiUser>(async (resolve) => {
        const user = await fetchUser()
        await findOne<User>('users', user.value?.id)
        .then( (response) => {
            resolve(response as unknown as StrapiUser)
        })
        .catch( (error) => {
            console.log("MyProfile fetch error: ", error)
        })
    })
})

Strapi-side requests related to this trial:

[2024-02-25 16:18:56.597] http: GET /api/users/me (11 ms) 200 
[2024-02-25 16:18:56.612] http: GET /api/users/2 (11 ms) 200

So, two requests are happening to pull off a SSR logged-in user profile page. Yes, I did also account other requests Nuxt makes on the same load.

In hindsight, I wonder if I can store the logged-in user id in a cookie? Is there a security issue there?

@kalnode kalnode added the question Further information is requested label Feb 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant