nuxt / nuxt

The Intuitive Vue Framework.
https://nuxt.com
MIT License
53.13k stars 4.87k forks source link

Defining `$fetch` within `useFetch()` doesnt pass cookies on SSR #27996

Open MickL opened 6 days ago

MickL commented 6 days ago

Environment


Reproduction

https://github.com/MickL/nuxt-bug-use-fetch-custom-fetch

Describe the bug

When setting $fetch param within useFetch() options Nuxt does not forward cookies on SSR:

await useFetch(url, {
    ...options,
    $fetch, // Or `$fetch: useNuxtApp().$customFetch`
  });

On the server (separate Nitro / H3 app) the cookie is undefined:

export default defineEventHandler(async (event) => {
   console.log(getCookie(event, 'auth_session'));
});

This happens only on SSR. When the Nuxt app is already loaded in the browser and navigating between pages everything works as expected.

Additional context

Potentially this is also an issue of using routeRules proxy?

danielroe commented 6 days ago

When using a custom fetch, Nuxt does not perform 'internal' fetches or pass on headers automatically - you will need to do this manually if that's the behaviour you want.

danekslama commented 6 days ago

@danielroe we have a similar issue. It seems that cookies are not somehow reactive in the newest version. It was working before the update to 3.12.2.

Tested on 3.11.2 and working correctly.

manniL commented 6 days ago

@danekslama this is probably https://github.com/nuxt/nuxt/issues/27660

MickL commented 5 days ago

@danielroe

How can I have a custom fetch but with all the behavior as before? The docs didnt say anything about the downsides of missing functionality when using custom fetch. Is there any example that has custom fetch but with everything as before?

I just want to have an interceptor that redirects if any response is 401 but this seems to be the most complicated thing ever :(

Aareksio commented 2 days ago

@MickL The docs are not very clear, but you need to recreate the functionality you need: https://nuxt.com/docs/getting-started/data-fetching#passing-headers-and-cookies

In practice, for cookies, you need something akin to:

export const myFetch = $fetch.create({
  async onRequest(ctx) {
    if (import.meta.server) {
      // Forward cookies to the target
      ctx.options.headers = useRequestHeaders(['cookie'])

      // Save event for later, we need it to forward response cookies
      ;(ctx as any).event = useRequestEvent()
    }
  },
  async onResponse(ctx) {
    if (import.meta.server && ctx.response.headers.get('set-cookie')) {
      // Forward response cookies to the client
      const cookieHeader = ctx.response.headers.get('set-cookie')!
      appendResponseHeader((ctx as any).event, 'set-cookie', cookieHeader)
    }
  },
})

After investigating your reproduction, it does not forward any cookie on SSR. You must have been setting it via client-side request in your tests:

Reload page 1: Cookie is not set Navigate to page 2, back to page 1: Cookie is set