packages/core/useFetch/index.md
Reactive Fetch API provides the ability to abort requests, intercept requests before
they are fired, automatically refetch requests when the url changes, and create your own useFetch with predefined options.
<CourseLink href="https://vueschool.io/lessons/vueuse-utilities-usefetch-and-reactify?friend=vueuse">Learn useFetch with this FREE video lesson from Vue School!</CourseLink>
::: tip
When using with Nuxt 3, this function will NOT be auto imported in favor of Nuxt's built-in useFetch(). Use explicit import if you want to use the function from VueUse.
:::
The useFetch function can be used by simply providing a url. The url can be either a string or a ref. The data object will contain the result of the request, the error object will contain any errors, and the isFetching object will indicate if the request is loading.
import { useFetch } from '@vueuse/core'
const { isFetching, error, data } = useFetch(url)
useFetch can also be awaited just like a normal fetch. Note that whenever a component is asynchronous, whatever component that uses
it must wrap the component in a <Suspense> tag. You can read more about the suspense api in the Official Vue 3 Docs
import { useFetch } from '@vueuse/core'
// ---cut---
const { isFetching, error, data } = await useFetch(url)
Using a ref for the url parameter will allow the useFetch function to automatically trigger another request when the url is changed.
import { useFetch } from '@vueuse/core'
// ---cut---
const url = ref('https://my-api.com/user/1')
const { data } = useFetch(url, { refetch: true })
url.value = 'https://my-api.com/user/2' // Will trigger another request
Setting the immediate option to false will prevent the request from firing until the execute function is called.
import { useFetch } from '@vueuse/core'
// ---cut---
const { execute } = useFetch(url, { immediate: false })
execute()
A request can be aborted by using the abort function from the useFetch function. The canAbort property indicates if the request can be aborted.
import { useFetch } from '@vueuse/core'
// ---cut---
const { abort, canAbort } = useFetch(url)
setTimeout(() => {
if (canAbort.value)
abort()
}, 100)
A request can also be aborted automatically by using timeout property. It will call abort function when the given timeout is reached.
import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, { timeout: 100 })
The beforeFetch option can intercept a request before it is sent and modify the request options and url.
import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, {
async beforeFetch({ url, options, cancel }) {
const myToken = await getMyToken()
if (!myToken)
cancel()
options.headers = {
...options.headers,
Authorization: `Bearer ${myToken}`,
}
return {
options,
}
},
})
The afterFetch option can intercept the response data before it is updated.
import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, {
afterFetch(ctx) {
if (ctx.data.title === 'HxH')
ctx.data.title = 'Hunter x Hunter' // Modifies the response data
return ctx
},
})
The onFetchError option can intercept the response data and error before it is updated when updateDataOnError is set to true.
import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, {
updateDataOnError: true,
onFetchError(ctx) {
// ctx.data can be null when 5xx response
if (ctx.data === null)
ctx.data = { title: 'Hunter x Hunter' } // Modifies the response data
ctx.error = new Error('Custom Error') // Modifies the error
return ctx
},
})
console.log(data.value) // { title: 'Hunter x Hunter' }
The request method and return type can be set by adding the appropriate methods to the end of useFetch
import { useFetch } from '@vueuse/core'
// ---cut---
// Request will be sent with GET method and data will be parsed as JSON
const { data } = useFetch(url).get().json()
// Request will be sent with POST method and data will be parsed as text
const { data } = useFetch(url).post().text()
// Or set the method using the options
// Request will be sent with GET method and data will be parsed as blob
const { data } = useFetch(url, { method: 'GET' }, { refetch: true }).blob()
The createFetch function will return a useFetch function with whatever pre-configured options that are provided to it. This is useful for interacting with API's throughout an application that uses the same base URL or needs Authorization headers.
import { createFetch } from '@vueuse/core'
// ---cut---
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
options: {
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
},
fetchOptions: {
mode: 'cors',
},
})
const { isFetching, error, data } = useMyFetch('users')
If you want to control the behavior of beforeFetch, afterFetch, onFetchError between the pre-configured instance and newly spawned instance. You can provide a combination option to toggle between overwrite or chaining.
import { createFetch } from '@vueuse/core'
// ---cut---
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
combination: 'overwrite',
options: {
// beforeFetch in pre-configured instance will only run when the newly spawned instance do not pass beforeFetch
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
},
})
// use useMyFetch beforeFetch
const { isFetching, error, data } = useMyFetch('users')
// use custom beforeFetch
const { isFetching, error, data } = useMyFetch('users', {
async beforeFetch({ url, options, cancel }) {
const myToken = await getMyToken()
if (!myToken)
cancel()
options.headers = {
...options.headers,
Authorization: `Bearer ${myToken}`,
}
return {
options,
}
},
})
You can re-execute the request by calling the execute method in afterFetch or onFetchError. Here is a simple example of refreshing a token:
import { createFetch } from '@vueuse/core'
// ---cut---
let isRefreshing = false
const refreshSubscribers: Array<() => void> = []
const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
options: {
async beforeFetch({ options }) {
const myToken = await getMyToken()
options.headers.Authorization = `Bearer ${myToken}`
return { options }
},
afterFetch({ data, response, context, execute }) {
if (needRefreshToken) {
if (!isRefreshing) {
isRefreshing = true
refreshToken().then((newToken) => {
if (newToken.value) {
isRefreshing = false
setMyToken(newToken.value)
onRefreshed()
}
else {
refreshSubscribers.length = 0
// handle refresh token error
}
})
}
return new Promise((resolve) => {
addRefreshSubscriber(() => {
execute().then((response) => {
resolve({ data, response })
})
})
})
}
return { data, response }
},
// or use onFetchError with updateDataOnError
updateDataOnError: true,
onFetchError({ error, data, response, context, execute }) {
// same as afterFetch
return { error, data }
},
},
fetchOptions: {
mode: 'cors',
},
})
async function refreshToken() {
const { data, execute } = useFetch<string>('refresh-token', {
immediate: false,
})
await execute()
return data
}
function onRefreshed() {
refreshSubscribers.forEach(callback => callback())
refreshSubscribers.length = 0
}
function addRefreshSubscriber(callback: () => void) {
refreshSubscribers.push(callback)
}
const { isFetching, error, data } = useMyFetch('users')
The onFetchResponse and onFetchError will fire on fetch request responses and errors respectively.
import { useFetch } from '@vueuse/core'
// ---cut---
const { onFetchResponse, onFetchError } = useFetch(url)
onFetchResponse((response) => {
console.log(response.status)
})
onFetchError((error) => {
console.error(error.message)
})