Skip to content

Commit 85a3d0d

Browse files
committed
Rework fetchBaseQuery API ref and document new options
1 parent 71003b8 commit 85a3d0d

File tree

2 files changed

+157
-65
lines changed

2 files changed

+157
-65
lines changed

docs/rtk-query/api/fetchBaseQuery.mdx

Lines changed: 156 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11,76 +11,151 @@ description: 'RTK Query > API: fetchBaseQuery reference'
1111

1212
# `fetchBaseQuery`
1313

14-
This is a very small wrapper around [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) that aims to simplify requests. It is not a full-blown replacement for `axios`, `superagent`, or any other more heavy-weight library, but it will cover the large majority of your needs.
14+
This is a very small wrapper around [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) that aims to simplify HTTP requests. It is not a full-blown replacement for `axios`, `superagent`, or any other more heavyweight library, but it will cover the vast majority of your HTTP request needs.
1515

16-
It takes all standard options from fetch's [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) interface, as well as `baseUrl`, a `prepareHeaders` function, an optional `fetch` function, a `paramsSerializer` function, and a `timeout`.
16+
`fetchBaseQuery` is a factory function that generates a data fetching method compatible with RTK Query's `baseQuery` confiugration option. It takes all standard options from fetch's [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) interface, as well as `baseUrl`, a `prepareHeaders` function, an optional `fetch` function, a `paramsSerializer` function, and a `timeout`.
1717

18-
- `baseUrl` _(required)_
19-
- Typically a string like `https://api.your-really-great-app.com/v1/`. If you don't provide a `baseUrl`, it defaults to a relative path from where the request is being made. You should most likely _always_ specify this.
20-
- `prepareHeaders` _(optional)_
18+
## Basic Usage
2119

22-
- Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors.
23-
24-
- ```ts title="prepareHeaders signature" no-transpile
25-
;(
26-
headers: Headers,
27-
api: {
28-
getState: () => unknown
29-
extra: unknown
30-
endpoint: string
31-
type: 'query' | 'mutation'
32-
forced: boolean | undefined
33-
}
34-
) => Headers
35-
```
36-
37-
- `paramsSerializer` _(optional)_
38-
- A function that can be used to apply custom transformations to the data passed into [`params`](#setting-the-query-string). If you don't provide this, `params` will be given directly to `new URLSearchParms()`. With some API integrations, you may need to leverage this to use something like the [`query-string`](https://github.com/sindresorhus/query-string) library to support different array types.
39-
- `fetchFn` _(optional)_
40-
- A fetch function that overrides the default on the window. Can be useful in SSR environments where you may need to leverage `isomorphic-fetch` or `cross-fetch`.
41-
- `timeout` _(optional)_
42-
- A number in milliseconds that represents the maximum time a request can take before timing out.
43-
44-
```ts title="Return types of fetchBaseQuery" no-transpile
45-
Promise<{
46-
data: any;
47-
error?: undefined;
48-
meta?: { request: Request; response: Response };
49-
} | {
50-
error: {
51-
status: number;
52-
data: any;
53-
};
54-
data?: undefined;
55-
meta?: { request: Request; response: Response };
56-
}>
57-
```
58-
59-
### Using `fetchBaseQuery`
60-
61-
To use it, import it when you are [creating an API service definition](../../tutorials/rtk-query#create-an-api-service).
20+
To use it, import it when you are [creating an API service definition](../../tutorials/rtk-query#create-an-api-service), call it as `fetchBaseQuery(options)`, and pass the result as the `baseQuery` field in `createApi`:
6221

6322
```ts title="src/services/pokemon.ts"
6423
// Or from '@reduxjs/toolkit/query/react'
6524
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
6625

6726
export const pokemonApi = createApi({
68-
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), // Set the baseUrl for every endpoint below
27+
// Set the baseUrl for every endpoint below
28+
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
6929
endpoints: (builder) => ({
7030
getPokemonByName: builder.query({
71-
query: (name: string) => `pokemon/${name}`, // Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
31+
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
32+
query: (name: string) => `pokemon/${name}`,
7233
}),
7334
updatePokemon: builder.mutation({
7435
query: ({ name, patch }) => ({
7536
url: `pokemon/${name}`,
76-
method: 'PATCH', // When performing a mutation, you typically use a method of PATCH/PUT/POST/DELETE for REST endpoints
77-
body: patch, // fetchBaseQuery automatically adds `content-type: application/json` to the Headers and calls `JSON.stringify(patch)`
37+
// When performing a mutation, you typically use a method of
38+
// PATCH/PUT/POST/DELETE for REST endpoints
39+
method: 'PATCH',
40+
// fetchBaseQuery automatically adds `content-type: application/json` to
41+
// the Headers and calls `JSON.stringify(patch)`
42+
body: patch,
7843
}),
7944
}),
8045
}),
8146
})
8247
```
8348

49+
## Signature
50+
51+
```ts title="fetchBaseQuery signature" no-transpile
52+
type FetchBaseQuery = (
53+
args: FetchBaseQueryArgs
54+
) => (
55+
args: string | FetchArgs,
56+
api: BaseQueryApi,
57+
extraOptions: ExtraOptions
58+
) => FetchBaseQueryResult
59+
60+
type FetchBaseQueryArgs = {
61+
baseUrl?: string
62+
prepareHeaders?: (
63+
headers: Headers,
64+
api: Pick<
65+
BaseQueryApi,
66+
'getState' | 'extra' | 'endpoint' | 'type' | 'forced'
67+
>
68+
) => MaybePromise<Headers | void>
69+
fetchFn?: (
70+
input: RequestInfo,
71+
init?: RequestInit | undefined
72+
) => Promise<Response>
73+
paramsSerializer?: (params: Record<string, any>) => string
74+
isJsonContentType?: (headers: Headers) => boolean
75+
jsonContentType?: string
76+
timeout?: number
77+
} & RequestInit
78+
79+
type FetchBaseQueryResult = Promise<
80+
| {
81+
data: any
82+
error?: undefined
83+
meta?: { request: Request; response: Response }
84+
}
85+
| {
86+
error: {
87+
status: number
88+
data: any
89+
}
90+
data?: undefined
91+
meta?: { request: Request; response: Response }
92+
}
93+
>
94+
```
95+
96+
## Parameters
97+
98+
### `baseUrl`
99+
100+
_(required)_
101+
102+
Typically a string like `https://api.your-really-great-app.com/v1/`. If you don't provide a `baseUrl`, it defaults to a relative path from where the request is being made. **You should most likely _always_ specify this**.
103+
104+
### `prepareHeaders`
105+
106+
_(optional)_
107+
108+
Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors.
109+
110+
You can mutate the `headers` argument directly, and returning it is optional.
111+
112+
```ts title="prepareHeaders signature" no-transpile
113+
type prepareHeaders = (
114+
headers: Headers,
115+
api: {
116+
getState: () => unknown
117+
extra: unknown
118+
endpoint: string
119+
type: 'query' | 'mutation'
120+
forced: boolean | undefined
121+
}
122+
) => Headers | void
123+
```
124+
125+
### `paramsSerializer`
126+
127+
_(optional)_
128+
129+
A function that can be used to apply custom transformations to the data passed into [`params`](#setting-the-query-string). If you don't provide this, `params` will be given directly to `new URLSearchParms()`. With some API integrations, you may need to leverage this to use something like the [`query-string`](https://github.com/sindresorhus/query-string) library to support different array types.
130+
131+
### `fetchFn`
132+
133+
_(optional)_
134+
135+
A fetch function that overrides the default on the window. Can be useful in SSR environments where you may need to leverage `isomorphic-fetch` or `cross-fetch`.
136+
137+
### `timeout`
138+
139+
_(optional)_
140+
141+
A number in milliseconds that represents the maximum time a request can take before timing out.
142+
143+
### `isJsonContentType`
144+
145+
_(optional)_
146+
147+
A callback that receives a `Headers` object and determines the `body` field of the `FetchArgs` argument should be stringified via `JSON.stringify()`.
148+
149+
The default implementation inspects the `content-type` header, and will match values like `"application/json"` and `"application/vnd.api+json"`.
150+
151+
### `jsonContentType`
152+
153+
_(optional)_
154+
155+
Used when automatically setting the `content-type` header for a request with a jsonifiable body that does not have an explicit `content-type` header. Defaults to `"application/json"`.
156+
157+
## Common Usage Patterns
158+
84159
### Setting default headers on requests
85160

86161
The most common use case for `prepareHeaders` would be to automatically include `authorization` headers for your API requests.
@@ -108,22 +183,20 @@ const baseQuery = fetchBaseQuery({
108183
})
109184
```
110185

111-
### Individual query options
112-
113-
There is more behavior that you can define on a per-request basis that extends the default options available to the `RequestInit` interface.
186+
## Individual query options
114187

115-
- [`params`](#setting-the-query-string)
116-
- [`body`](#setting-the-body)
117-
- [`responseHandler`](#parsing-a-Response)
118-
- [`validateStatus`](#handling-non-standard-response-status-codes)
119-
- [`timeout`](#adding-a-custom-timeout-to-requests)
188+
There is more behavior that you can define on a per-request basis. The `query` field may return an object containing any of the default `fetch` options available to the `RequestInit` interface, as well as these additional options:
120189

121190
```ts title="endpoint request options"
122191
interface FetchArgs extends RequestInit {
123192
url: string
124193
params?: Record<string, any>
125194
body?: any
126-
responseHandler?: 'json' | 'text' | ((response: Response) => Promise<any>)
195+
responseHandler?:
196+
| 'json'
197+
| 'text'
198+
| `content-type`
199+
| ((response: Response) => Promise<any>)
127200
validateStatus?: (response: Response, body: any) => boolean
128201
timeout?: number
129202
}
@@ -189,7 +262,19 @@ By default, `fetchBaseQuery` assumes that every request you make will be `json`,
189262

190263
### Parsing a Response
191264

192-
By default, `fetchBaseQuery` assumes that every `Response` you get will be parsed as `json`. In the event that you don't want that to happen, you can specify an alternative response handler like `text`, or take complete control and use a custom function that accepts the raw `Response` object &mdash; allowing you to use any [`Response` method](https://developer.mozilla.org/en-US/docs/Web/API/Response).
265+
By default, `fetchBaseQuery` assumes that every `Response` you get will be parsed as `json`. In the event that you don't want that to happen, you can customize the behavior by specifying an alternative response handler like `text`, or take complete control and use a custom function that accepts the raw `Response` object &mdash; allowing you to use any [`Response` method](https://developer.mozilla.org/en-US/docs/Web/API/Response).
266+
267+
The `responseHandler` field can be either:
268+
269+
```ts
270+
type ResponseHandler =
271+
| 'content-type'
272+
| 'json'
273+
| 'text'
274+
| ((response: Response) => Promise<any>)
275+
```
276+
277+
The `"json"` and `"text"` values instruct `fetchBaseQuery` to the corresponding fetch response methods for reading the body. `content-type` will check the header field to first determine if this appears to be JSON, and then use one of those two methods. The callback allows you to process the body yourself.
193278

194279
```ts title="Parse a Response as text"
195280
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
@@ -200,7 +285,8 @@ export const customApi = createApi({
200285
getUsers: builder.query({
201286
query: () => ({
202287
url: `users`,
203-
responseHandler: (response) => response.text(), // This is the same as passing 'text'
288+
// This is the same as passing 'text'
289+
responseHandler: (response) => response.text(),
204290
}),
205291
}),
206292
}),
@@ -219,13 +305,16 @@ By default, `fetchBaseQuery` will `reject` any `Response` that does not have a s
219305
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
220306
221307
export const customApi = createApi({
222-
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }), // Set the baseUrl for every endpoint below
308+
// Set the baseUrl for every endpoint below
309+
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
223310
endpoints: (builder) => ({
224311
getUsers: builder.query({
225312
query: () => ({
226313
url: `users`,
314+
// Example: we have a backend API always returns a 200,
315+
// but sets an `isError` property when there is an error.
227316
validateStatus: (response, result) =>
228-
response.status === 200 && !result.isError, // Our tricky API always returns a 200, but sets an `isError` property when there is an error.
317+
response.status === 200 && !result.isError,
229318
}),
230319
}),
231320
}),
@@ -240,12 +329,15 @@ By default, `fetchBaseQuery` has no default timeout value set, meaning your requ
240329
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
241330
242331
export const api = createApi({
243-
baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }), // Set a default timeout of 10 seconds
332+
// Set a default timeout of 10 seconds
333+
baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }),
244334
endpoints: (builder) => ({
245335
getUsers: builder.query({
246336
query: () => ({
247337
url: `users`,
248-
timeout: 1000, // We know the users endpoint is _really fast_ because it's always cached. We can assume if its over > 1000ms, something is wrong and we should abort the request.
338+
// Example: we know the users endpoint is _really fast_ because it's always cached.
339+
// We can assume if its over > 1000ms, something is wrong and we should abort the request.
340+
timeout: 1000,
249341
}),
250342
}),
251343
}),

packages/toolkit/src/query/fetchBaseQuery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ export type FetchBaseQueryMeta = { request: Request; response?: Response }
176176
* @param {(headers: Headers) => boolean} isJsonContentType
177177
* An optional predicate function to determine if `JSON.stringify()` should be called on the `body` arg of `FetchArgs`
178178
*
179-
* @param {string} jsonContentType Defaults to `application/json`. Used when automatically setting the content-type header for a request with a jsonifiable body that does not have an explicit content-type header.
179+
* @param {string} jsonContentType Used when automatically setting the content-type header for a request with a jsonifiable body that does not have an explicit content-type header. Defaults to `application/json`.
180180
*
181181
* @param {number} timeout
182182
* A number in milliseconds that represents the maximum time a request can take before timing out.

0 commit comments

Comments
 (0)