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): respect custom timeout in useFetch #24364

Merged
merged 11 commits into from Nov 20, 2023
1 change: 1 addition & 0 deletions docs/3.api/2.composables/use-fetch.md
Expand Up @@ -80,6 +80,7 @@ const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
- `body`: Request body - automatically stringified (if an object is passed).
- `headers`: Request headers.
- `baseURL`: Base URL for the request.
- `timeout`: Milliseconds to automatically abort request

::callout
All fetch options can be given a `computed` or `ref` value. These will be watched and new requests made automatically with any new values if they are updated.
Expand Down
10 changes: 10 additions & 0 deletions packages/nuxt/src/app/composables/fetch.ts
Expand Up @@ -140,6 +140,16 @@ export function useFetch<
controller?.abort?.()
controller = typeof AbortController !== 'undefined' ? new AbortController() : {} as AbortController

/**
* workaround for `timout` not working due to custom abort controller
* TODO: remove this when upstream issue is resolved
* @see https://github.com/unjs/ofetch/issues/326
* @see https://github.com/unjs/ofetch/blob/bb2d72baa5d3f332a2185c20fc04e35d2c3e258d/src/fetch.ts#L152
*/
if (toValue(opts.timeout)) {
setTimeout(() => controller.abort(), toValue(opts.timeout));
danielroe marked this conversation as resolved.
Show resolved Hide resolved
}

let _$fetch = opts.$fetch || globalThis.$fetch

// Use fetch with request context and headers for server direct API calls
Expand Down
10 changes: 10 additions & 0 deletions test/nuxt/composables.test.ts
Expand Up @@ -266,6 +266,16 @@ describe('useFetch', () => {
await useFetch('/api/test', { params: { id: ref('3') } }, '')
expect.soft(getPayloadEntries()).toBe(baseCount + 3)
})

it('should timeout', async () => {
const { status, error } = await useFetch(
() => new Promise(resolve => setTimeout(resolve, 5000)),
{ timeout: 1 }
)
await new Promise(resolve => setTimeout(resolve, 2))
expect(status.value).toBe('error')
expect(error.value).toMatchInlineSnapshot('[Error: [GET] "[object Promise]": <no response> The operation was aborted.]')
})
})

describe('errors', () => {
Expand Down