import type { FetchError } from 'ofetch' import type { AvailableRouterMethod, NitroFetchOptions, NitroFetchRequest, TypedInternalResponse } from 'nitropack' import type { Ref } from 'vue' import { computed, reactive, unref } from 'vue' import { hash } from 'ohash' import { useRequestFetch } from './ssr' import type { AsyncData, AsyncDataOptions, KeysOf, MultiWatchSources, PickFrom, _Transform } from './asyncData' import { useAsyncData } from './asyncData' export type FetchResult> = TypedInternalResponse type ComputedOptions> = { [K in keyof T]: T[K] extends Function ? T[K] : T[K] extends Record ? ComputedOptions | Ref | T[K] : Ref | T[K] } type ComputedFetchOptions> = ComputedOptions> export interface UseFetchOptions< ResT, DataT = ResT, PickKeys extends KeysOf = KeysOf, R extends NitroFetchRequest = string & {}, M extends AvailableRouterMethod = AvailableRouterMethod > extends Omit, 'watch'>, ComputedFetchOptions { key?: string $fetch?: typeof globalThis.$fetch watch?: MultiWatchSources | false } export function useFetch< ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod = 'get' extends AvailableRouterMethod ? 'get' : AvailableRouterMethod, _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf > ( request: Ref | ReqT | (() => ReqT), opts?: UseFetchOptions<_ResT, DataT, PickKeys, ReqT, Method> ): AsyncData, ErrorT | null> export function useFetch< ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod = 'get' extends AvailableRouterMethod ? 'get' : AvailableRouterMethod, _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf > ( request: Ref | ReqT | (() => ReqT), arg1?: string | UseFetchOptions<_ResT, DataT, PickKeys, ReqT, Method>, arg2?: string ) { const [opts = {}, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2] const _key = opts.key || hash([autoKey, unref(opts.baseURL), typeof request === 'string' ? request : '', unref(opts.params || opts.query)]) if (!_key || typeof _key !== 'string') { throw new TypeError('[nuxt] [useFetch] key must be a string: ' + _key) } if (!request) { throw new Error('[nuxt] [useFetch] request is missing.') } const key = _key === autoKey ? '$f' + _key : _key const _request = computed(() => { let r = request if (typeof r === 'function') { r = r() } return unref(r) }) if (!opts.baseURL && typeof _request.value === 'string' && _request.value.startsWith('//')) { throw new Error('[nuxt] [useFetch] the request URL must not start with "//".') } const { server, lazy, default: defaultFn, transform, pick, watch, immediate, ...fetchOptions } = opts const _fetchOptions = reactive({ ...fetchOptions, cache: typeof opts.cache === 'boolean' ? undefined : opts.cache }) const _asyncDataOptions: AsyncDataOptions<_ResT, DataT, PickKeys> = { server, lazy, default: defaultFn, transform, pick, immediate, watch: watch === false ? [] : [_fetchOptions, _request, ...(watch || [])] } let controller: AbortController const asyncData = useAsyncData<_ResT, ErrorT, DataT, PickKeys>(key, () => { controller?.abort?.() controller = typeof AbortController !== 'undefined' ? new AbortController() : {} as AbortController const isLocalFetch = typeof _request.value === 'string' && _request.value.startsWith('/') let _$fetch = opts.$fetch || globalThis.$fetch // Use fetch with request context and headers for server direct API calls if (process.server && !opts.$fetch && isLocalFetch) { _$fetch = useRequestFetch() } return _$fetch(_request.value, { signal: controller.signal, ..._fetchOptions } as any) as Promise<_ResT> }, _asyncDataOptions) return asyncData } export function useLazyFetch< ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod = 'get' extends AvailableRouterMethod ? 'get' : AvailableRouterMethod, _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf > ( request: Ref | ReqT | (() => ReqT), opts?: Omit, 'lazy'> ): AsyncData, ErrorT | null> export function useLazyFetch< ResT = void, ErrorT = FetchError, ReqT extends NitroFetchRequest = NitroFetchRequest, Method extends AvailableRouterMethod = 'get' extends AvailableRouterMethod ? 'get' : AvailableRouterMethod, _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf > ( request: Ref | ReqT | (() => ReqT), arg1?: string | Omit, 'lazy'>, arg2?: string ) { const [opts, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2] return useFetch(request, { ...opts, lazy: true }, // @ts-expect-error we pass an extra argument with the resolved auto-key to prevent another from being injected autoKey) }