Skip to content

Commit

Permalink
chore(useFetch): add tests and update docs for useFetch (vitest-dev#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
wheatjs committed Mar 13, 2021
1 parent 38677c5 commit ae7821d
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 51 deletions.
119 changes: 69 additions & 50 deletions packages/core/useFetch/index.md
Expand Up @@ -2,94 +2,113 @@
category: Browser
---


# useFetch

Reactive [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) with support for [aborting requests](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort), in browsers that support it.
Reactive [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/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.

[[toc]]

## Usage

### Basic Usage
The `useFetch` funciton 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.
```ts
import { useFetch } from '@vueuse/core'

const { isFinished, statusCode, error, data } = useFetch(url)
const { isFetching, error, data } = useFetch(url)
```

Prevent auto-calling the fetch request and do it manually instead

### Refetching on URL change
Using a `ref` for the url parameter will allow the `useFetch` function to automatically trigger another
request when the url is changed.
```ts
import { useFetch } from '@vueuse/core'
const url = ref('https://my-api.com/user/1')

const { execute, data } = useFetch(url, { immediate: false })
const { data } = useFetch(url, { refetch: true })

execute()
url.value = 'https://my-api.com/user/2' // Will trigger another request
```

Fetch as Blob

### Prevent request from firing immediately
Setting the `immediate` option to false will prevent the request from firing until the `execute`
function is called.
```ts
import { useFetch } from '@vueuse/core'
const { execute } = useFetch(url, { immediate: false })

const { execute, data } = useFetch(url).blob()
execute()
```

Post a JSON

### Aborting a request
A request can be aborted by using the `abort` function from the `useFetch` function. The `canAbort` property indicates
if the request can be aborted
```ts
import { useFetch } from '@vueuse/core'
const { abort, canAbort } = useFetch(url)

const { execute, data } = useFetch(url)
.post({ message: 'Hello' })
.json()
setTimeout(() => {
if (canAbort.value)
abort()
}, 100)
```

Abort a fetch

### Intercepting a request
The `beforeFetch` option can intercept a request before it is sent and modify the request options and url.
```ts
import { useFetch } from '@vueuse/core'
const { data } = useFetch(url, {
async beforeFetch({ url, options, cancel }) {
const myToken = await getMyToken()

const { execute, data, isFetching, abort } = useFetch(url)
if (!myToken)
cancel()

setTimeout(() => {
// timeout!
abort()
}, 1000)
options.headers.Authorization = `Bearer ${myToken}`

return {
options
}
}
})
```

Automatically refetch when your URL is a ref
### Setting the request method and return type
The requset method and return type can be set by adding the appropite methods to the end of `useFetch`

```ts
import { useFetch } from '@vueuse/core'
// Request will be sent with GET method and data will be parsed as JSON
const { data } = useFetch(url).get().json()

const url = ref('https://httpbin.org/get')
// Request will be sent with POST method and data will be parsed as text
const { data } = useFetch(url).post().text()

const { data } = useFetch(url, { refetch: true })
// Or set the method using the options

setTimeout(() => {
// Request will be fetched again
url.value = 'https://httpbin.org/status/500'
}, 5000)
// Request will be sent with GET method and data will be parsed as blob
const { data } = useFetch(url, { method: 'GET' }, { refetch: true }).blob()
```

Create a custom `useFetch` instance with default values

### Creating a custom instance
The `createFetch` function will return a useFetch function with whatever preconfigured options that are provided to it.
This is useful for interacting with API's thoughout an application that uses the same base URL or needs Authorization headers.
```ts
// foo.ts
import { createFetch } from '@vueuse/core'

export const useMyFetch = createFetch({
baseUrl: 'https://my-api.com',
headers: {
Authorization: 'my-token',
}
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',
},
})
```

```ts
// bar.ts
import { useMyFetch } from './foo'

// will request `https://my-api.com/posts` with token
const { data } = useFetch('/posts')
const { isFetching, error, data } = useMyFetch('users')
```

<!--FOOTER_STARTS-->
Expand Down
34 changes: 34 additions & 0 deletions packages/core/useFetch/index.test.ts
Expand Up @@ -94,4 +94,38 @@ describe('useFetch', () => {
expect(fetchMock.mock.calls[0][1]!.headers).toMatchObject({ Authorization: 'test', 'Accept-Language': 'en-US' })
expect(fetchMock.mock.calls[0][0]).toEqual('https://example.com/test')
})

test('should run the beforeFetch function and add headers to the request', async() => {
fetchMock.mockResponse('')

const { isFinished } = useFetch('https://example.com', { headers: { 'Accept-Language': 'en-US' } }, {
beforeFetch({ options }) {
options.headers = {
...options.headers,
Authorization: 'my-auth-token',
}

return { options }
},
})

await when(isFinished).toBe(true)

expect(fetchMock.mock.calls[0][1]!.headers).toMatchObject({ Authorization: 'my-auth-token', 'Accept-Language': 'en-US' })
})

test('should run the beforeFetch function and cancel the request', async() => {
fetchMock.mockResponse('')

const { execute } = useFetch('https://example.com', {
immediate: false,
beforeFetch({ cancel }) {
cancel()
},
})

await execute()

expect(fetchMock).toBeCalledTimes(0)
})
})
2 changes: 1 addition & 1 deletion packages/core/useFetch/index.ts
Expand Up @@ -114,7 +114,7 @@ export interface UseFetchOptions {
/**
* Will run immediately before the fetch request is dispatched
*/
beforeFetch?: (ctx: BeforeFetchContext) => Promise<Partial<BeforeFetchContext>> | Partial<BeforeFetchContext>
beforeFetch?: (ctx: BeforeFetchContext) => Promise<Partial<BeforeFetchContext> | void> | Partial<BeforeFetchContext> | void
}

export interface CreateFetchOptions {
Expand Down

0 comments on commit ae7821d

Please sign in to comment.