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

fix(nuxt): deeply unwrap headers/query for useFetch key #24307

Merged
merged 7 commits into from Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 22 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,21 @@ 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) {
const unwrapped: Record<string, string> = {}
for (const key in obj) {
unwrapped[key] = toValue(obj[key as keyof typeof obj])
danielroe marked this conversation as resolved.
Show resolved Hide resolved
}
segments.push(unwrapped)
}
}
return segments
}
27 changes: 27 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,29 @@ 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') } }, '')
expect.soft(getPayloadEntries()).toBe(baseCount + 3)
})
})

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