Skip to content

Commit

Permalink
fix(nuxt): deeply unwrap headers/query for useFetch key (#24307)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored and manniL committed Dec 11, 2023
1 parent b501a99 commit 67f0402
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 4 deletions.
27 changes: 23 additions & 4 deletions packages/nuxt/src/app/composables/fetch.ts
@@ -1,7 +1,7 @@
import type { FetchError, FetchOptions } from 'ofetch'
import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod as _AvailableRouterMethod } from 'nitropack'
import type { MaybeRef, Ref } from 'vue'
import { computed, reactive, unref } from 'vue'
import { computed, reactive, toValue } from 'vue'
import { hash } from 'ohash'

import { useRequestFetch } from './ssr'
Expand Down Expand Up @@ -86,10 +86,10 @@ export function useFetch<
if (typeof r === 'function') {
r = r()
}
return unref(r)
return toValue(r)
})

const _key = opts.key || hash([autoKey, unref(opts.method as MaybeRef<string | undefined> | undefined)?.toUpperCase() || 'GET', unref(opts.baseURL), typeof _request.value === 'string' ? _request.value : '', unref(opts.params || opts.query), unref(opts.headers)])
const _key = opts.key || hash([autoKey, typeof _request.value === 'string' ? _request.value : '', ...generateOptionSegments(opts)])
if (!_key || typeof _key !== 'string') {
throw new TypeError('[nuxt] [useFetch] key must be a string: ' + _key)
}
Expand Down Expand Up @@ -144,7 +144,7 @@ export function useFetch<

// Use fetch with request context and headers for server direct API calls
if (import.meta.server && !opts.$fetch) {
const isLocalFetch = typeof _request.value === 'string' && _request.value.startsWith('/') && (!unref(opts.baseURL) || unref(opts.baseURL)!.startsWith('/'))
const isLocalFetch = typeof _request.value === 'string' && _request.value.startsWith('/') && (!toValue(opts.baseURL) || toValue(opts.baseURL)!.startsWith('/'))
if (isLocalFetch) {
_$fetch = useRequestFetch()
}
Expand Down Expand Up @@ -205,3 +205,22 @@ export function useLazyFetch<
// @ts-expect-error we pass an extra argument with the resolved auto-key to prevent another from being injected
autoKey)
}

function generateOptionSegments <_ResT, DataT, DefaultT>(opts: UseFetchOptions<_ResT, DataT, any, DefaultT, any, any>) {
const segments: Array<string | undefined | Record<string, string>> = [
toValue(opts.method as MaybeRef<string | undefined> | undefined)?.toUpperCase() || 'GET',
toValue(opts.baseURL),
]
for (const _obj of [opts.params || opts.query, opts.headers]) {
const obj = toValue(_obj)
if (!obj) { continue }

const unwrapped: Record<string, string> = {}
const iterator = Array.isArray(obj) ? obj : obj instanceof Headers ? obj.entries() : Object.entries(obj)
for (const [key, value] of iterator) {
unwrapped[toValue(key)] = toValue(value)
}
segments.push(unwrapped)
}
return segments
}
37 changes: 37 additions & 0 deletions test/nuxt/composables.test.ts
Expand Up @@ -38,6 +38,10 @@ registerEndpoint('/_nuxt/builds/meta/override.json', defineEventHandler(() => ({
},
prerendered: ['/specific-prerendered']
})))
registerEndpoint('/api/test', defineEventHandler((event) => ({
method: event.method,
headers: Object.fromEntries(event.headers.entries())
})))

describe('app config', () => {
it('can be updated', () => {
Expand Down Expand Up @@ -237,6 +241,39 @@ describe('useAsyncData', () => {
})
})

describe('useFetch', () => {
it('should match with/without computed values', async () => {
const nuxtApp = useNuxtApp()
const getPayloadEntries = () => Object.keys(nuxtApp.payload.data).length
const baseCount = getPayloadEntries()

await useFetch('/api/test')
expect(getPayloadEntries()).toBe(baseCount + 1)

/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { method: 'POST' }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { method: ref('POST') }, '')
expect.soft(getPayloadEntries()).toBe(baseCount + 2)

/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: { id: '3' } }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: { id: ref('3') } }, '')
const headers = new Headers()
headers.append('id', '3')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: [['id', '3']] }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: [['id', ref('3')]] }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: [[computed(() => 'id'), '3']] }, '')
expect.soft(getPayloadEntries()).toBe(baseCount + 3)
})
})

describe('errors', () => {
it('createError', () => {
expect(createError({ statusCode: 404 }).toJSON()).toMatchInlineSnapshot(`
Expand Down

0 comments on commit 67f0402

Please sign in to comment.