nuxt: `useFetch` data return null with mismatching client/request options

Environment

------------------------------
- Operating System: `Linux`
- Node Version:     `v16.14.2`
- Nuxt Version:     `3.0.0-27480123.4c77c88`
- Package Manager:  `yarn@1.22.18`
- Builder:          `vite`
- User Config:      `-`
- Runtime Modules:  `-`
- Build Modules:    `-`
------------------------------

Reproduction

In clean Nuxt project:

<template>
  <div>
    <NuxtWelcome />
  </div>
</template>

<script lang="ts" setup>
const [{ data: organization }, { data: repos }] = await Promise.all([
  useFetch(`https://api.github.com/orgs/nuxt`),
  useFetch(`https://api.github.com/orgs/nuxt/repos`)
])

console.log(organization.value) // null

const { data: organization } = await useFetch(`https://api.github.com/orgs/nuxt`)
console.log(organization.value) // null
</script>

Describe the bug

useFetch started returning null to the “data” property

Additional context

No response

Logs

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 24 (20 by maintainers)

Most upvoted comments

For giving manual key and baseURL to useFetch: (notice baseURL case)

await useFetch(`/api/v2/...`, { baseURL, key: 'countries-api' })

This will make keys same.


Interesting find @danielroe!

I would still assume it is a bug that the client-side is returning null instead of re-attempt to fetch!

It is not safe to remove baseURL or any other option from auto key generation. Two instances of useFetch with different baseURL will conflict this way.

Thinking about some possible improvements. Supporting proxy for me seems best approach. In the meantime, we should improve documentation and warn about this that if URL or options between client/server are not same, useFetch needs explicit key or will need to refetch (currently mismatch)

Ah. I’ve just seen what you’re doing with baseURL. The key is based on the URL so by changing the URL between server and client, there is a mismatch.

In your case, you could use useAsyncData and pick a key manually.

I had the exact same situation. Sharing here for future reference:

const headers = useRequestHeaders(['cookie'])

const { data, pending, error, refresh } = await useFetch<TResult>('/api/example', {
  headers
})

In SSR, useFetch fetches data fine on the server, but on client, it returns null and retrieves a hydration mismatch. It was because the headers on server were not the same on client. Adding a key solved the issue for me.

const { data, pending, error, refresh } = await useFetch<TResult>('/api/example', {
  headers,
  key: 'key'
})

I assume the key was generated not only on baseURL like OP’s case but also headers.

@danielroe Thanks a lot! I’ve opened nuxt/nuxt.js#13768 and have made a public repository in order to reproduce this issue.

Please reach out to me if you need anything, I’m happy to help in any way I can in order to resolve this. Nuxt 3 is looking great, I appreciate you, the team, and your hard work!

useFetch fires on the server, so you won’t see the result in your Network tab. And likely the issue is that that request doesn’t have a correct CSRF token or other header… See https://v3.nuxtjs.org/guide/features/data-fetching/#isomorphic-fetch-and-fetch for more info, and raise a discussion if you have any questions.

@misaon Can you also debug { error } value?

const [a, b] = await Promise.all([
  useFetch(`https://api.github.com/orgs/nuxt`),
  useFetch(`https://api.github.com/orgs/nuxt/repos`)
])

console.log('A:', a.data.value, a.error.value)
console.log('B:', b.data.vlue, b.error.value)

@tntsoft Would you create a new issue with a reproduction? (This is not related to this issue.)

@danielroe Try this:

<template>
  <pre>
    {{ xxx }}
  </pre>
  <pre>
    {{ yyy }}
  </pre>
</template>

<script lang="ts" setup>
const [{ data: xxx }, { data: yyy }] = await Promise.all([
  useFetch(`https://jsonplaceholder.typicode.com/todos/1`),
  useFetch(`https://api.github.com/orgs/nuxt/repos`)
])

console.log(xxx.value) // { userId: 1, id: 1, title: 'delectus aut autem', completed: false }
console.log(yyy.value) // null
</script>