unjs / nitro

Next Generation Server Toolkit. Create web servers with everything you need and deploy them wherever you prefer.
https://nitro.unjs.io
MIT License
5.72k stars 486 forks source link

Nuxt 3 - Nitro vercel-edge - Redirection problem on page load - URL parameter appended to URL #2515

Open CedsTrash opened 2 months ago

CedsTrash commented 2 months ago

Environment


Reproduction

I can't give you a minimal reproduction because it runs on Vercel Edge. I can just give you the link to the website: https://staging-vercel-edge.nxstar.ca

Describe the bug

Everything works fine locally and when I deploy to vercel with the "vercel" nitro preset. But when I do the same with the "vercel-edge" preset the problems starts:

If I reach any of the pages directly, I can see the content and then open my navigation menu and navigate to an other page. It works fine and the URL is now correct (without the appended URL parameter). But the problem appears again as soon as I refresh the page.

All my routes are in ISR mode and I use @nuxtjs/i18n with this config:

export default defineNuxtConfig({
    i18n: {
        defaultLocale: 'en',
        detectBrowserLanguage: false,
        langDir: 'lang',
        lazy: true,
        locales: [
          {
            code: 'fr',
            iso: 'fr-CA',
            file: 'fr.ts'
          },
          {
            code: 'en',
            iso: 'en-CA',
            file: 'en.ts'
          }
        ],
        customRoutes: 'config',
        pages: {
            '[url]': {
                en: '/[url]',
                fr: '/[url]'
            },
            'post/[url]': {
                en: '/post/[url]',
                fr: '/article/[url]'
            }
        },
        strategy: 'prefix_except_default',
        vueI18n: "./i18n.config.ts"
    },
    routeRules: {
        '/**': { isr: 3600 },
    }
})

And finally, page content is fetched from a headless CMS like this: pages/[url].vue

<script setup>
const config = useRuntimeConfig()
const route = useRoute()
const { locale } = useI18n()
const requestUrl = useRequestURL()

const url = ref({en: '/' + route.params.url, fr: '/fr/' + route.params.url})
const { data : page } = await useAsyncData(
    'page-${route.params.url}',
    () => $fetch(config.public.BASE_API_URL + '/collections/pages/entries?token=' + route.query.token + '&filter[site]=' + locale.value + '&filter[url]=' + url.value[locale.value])
)
</script>

I found out 2 people on the official Nuxt Discord server who point out to a but with the vercel-edge Nitro preset. Again, it all works perfectly until I activate the vercel-edge preset.

Thanks for your help.

Additional context

I want to use Vercel Edge in conjunction with ISR because I need the On-Demand ISR feature, and of course the lower latencies that edge functions offer.

Logs

No response

CedsTrash commented 2 months ago

Hi @pi0 How can I provide a reproduction for this since it's specific to the vercel-edge preset? Thanks.

CedsTrash commented 2 months ago

After playing a bit with the routeRules setting I now have a slighty different behavior:

routeRules: {
    '/**': { isr: 3600 },
    '/second-page': { swr: true, cache: { maxAge: 60 * 40 } },
    '/third-page': { isr: 3600, cache: { maxAge: 60 } },
    '/fourth-page': { cache: { maxAge: 60 } },
  },

Now I get this:

I disabled Vercel deployment protection so anyone can access the site without having to click the Vercel shared link first.

danielroe commented 2 months ago

@CedsTrash it would be best if you can reproduce this in pure Nitro (reproduction sandbox). 🙏

It's fine if you upload it to StackBlitz or a GitHub repo, and if it needs to be deployed to vercel to reproduce - we can test that.

CedsTrash commented 2 months ago

After briefly talking to @manniL, I've tried to set up a reproduction but I wasn't really sure about what to do: https://stackblitz.com/~/github.com/CedsTrash/nitro-vercel-edge-isr

And then I deployed it to Vercel: https://nitro-vercel-edge-isr.vercel.app/

The current preset is "vercel", because if I set it to "vercel_edge" I get a 404: NOT FOUND. I don't see any URL parameter being appended here, but again, I'm not sure if this is good reproduction.

Thanks for your insights.

CedsTrash commented 1 month ago

Hi everyone,

I just updated the code and also created a dedicated project on Vercel with a new domain: https://staging-vercel-edge.nxstar.ca

I've changed a few things after talking with @manniL, in order to better manage caching.

pages/[url].vue now looks like this:

<script setup>
const route = useRoute()
const { locale } = useI18n()
const requestUrl = useRequestURL()

const url = ref({en: '/' + route.params.url, fr: '/fr/' + route.params.url})

const { data: page } = await useAsyncData(
    `page-${route.params.url}`,
    () => $fetch(`/api/bff`, {
      method: 'POST',
      body: {
        locale: locale.value,
        url: url.value[locale.value],
        cacheKey: `page-${route.params.url}`,
      }
    })
)
</script>

My /api/bff route looks like this:

export default defineCachedEventHandler(async (event) => {
    const config = useRuntimeConfig()

    const body = await readBody(event)
    const result = await $fetch(config.public.BASE_API_URL + '/collections/pages/entries?filter[site]=' + body.locale + '&filter[url]=' + body.url)

    return {
        data: result.data,
        fetchedAt: new Date()
    }
}, {
    maxAge: 30,
    getKey: async (event) => {
        const body = await readBody(event)

        return body.cacheKey
    }
})

And my routes rules are as follow:

routeRules: {
  '/**': { isr: 60 },
  '/api/**': { isr: false },
},

I now have a better understanding on how all of these parts work together in terms of caching. But I still experience the same behavior on Vercel as soon as I switch to the vercel-edge preset.

CedsTrash commented 3 weeks ago

Does someone have a clue on what's going on? I'm really stuck on this one and could really use some help. Thanks!