fetchBaseQuery
これは、HTTP リクエストを簡略化することを目的とした、fetch
を非常に小さくラップしたものです。これは、axios
、superagent
、または他のよりヘビー級なライブラリの完全な代替品ではありませんが、HTTP リクエストのニーズの大部分をカバーします。
fetchBaseQuery
は、RTK Query の baseQuery
設定オプションと互換性のあるデータフェッチメソッドを生成するファクトリー関数です。fetch の RequestInit
インターフェースのすべての標準オプションに加えて、baseUrl
、prepareHeaders
関数、オプションの fetch
関数、paramsSerializer
関数、および timeout
を取ります。
基本的な使い方
これを使用するには、API サービス定義を作成するときにインポートし、fetchBaseQuery(options)
として呼び出し、結果を createApi
の baseQuery
フィールドとして渡します。
- TypeScript
- JavaScript
// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const pokemonApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
query: (name: string) => `pokemon/${name}`,
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
// When performing a mutation, you typically use a method of
// PATCH/PUT/POST/DELETE for REST endpoints
method: 'PATCH',
// fetchBaseQuery automatically adds `content-type: application/json` to
// the Headers and calls `JSON.stringify(patch)`
body: patch,
}),
}),
}),
})
// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const pokemonApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
// Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
query: (name) => `pokemon/${name}`,
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
// When performing a mutation, you typically use a method of
// PATCH/PUT/POST/DELETE for REST endpoints
method: 'PATCH',
// fetchBaseQuery automatically adds `content-type: application/json` to
// the Headers and calls `JSON.stringify(patch)`
body: patch,
}),
}),
}),
})
シグネチャ
type FetchBaseQuery = (
args: FetchBaseQueryArgs,
) => (
args: string | FetchArgs,
api: BaseQueryApi,
extraOptions: ExtraOptions,
) => FetchBaseQueryResult
type FetchBaseQueryArgs = {
baseUrl?: string
prepareHeaders?: (
headers: Headers,
api: Pick<
BaseQueryApi,
'getState' | 'extra' | 'endpoint' | 'type' | 'forced'
>,
) => MaybePromise<Headers | void>
fetchFn?: (
input: RequestInfo,
init?: RequestInit | undefined,
) => Promise<Response>
paramsSerializer?: (params: Record<string, any>) => string
isJsonContentType?: (headers: Headers) => boolean
jsonContentType?: string
timeout?: number
} & RequestInit
type FetchBaseQueryResult = Promise<
| {
data: any
error?: undefined
meta?: { request: Request; response: Response }
}
| {
error: {
status: number
data: any
}
data?: undefined
meta?: { request: Request; response: Response }
}
>
パラメータ
baseUrl
(必須)
通常は https://api.your-really-great-app.com/v1/
のような文字列です。baseUrl
を指定しない場合、リクエストが行われる場所からの相対パスがデフォルトになります。ほとんどの場合、常にこれを指定する必要があります。
prepareHeaders
(オプション)
すべてのリクエストにヘッダーを挿入できます。エンドポイントレベルでヘッダーを指定できますが、通常は authorization
のような共通ヘッダーをここに設定します。便利なメカニズムとして、2 番目の引数で getState
を使用して、認証トークンなど必要な情報を保存している場合に Redux ストアにアクセスできます。さらに、extra
、endpoint
、type
、および forced
にアクセスして、より詳細な条件付き動作をアンロックできます。
headers
引数を直接変更でき、それを返すのはオプションです。
type prepareHeaders = (
headers: Headers,
api: {
getState: () => unknown
extra: unknown
endpoint: string
type: 'query' | 'mutation'
forced: boolean | undefined
},
) => Headers | void
paramsSerializer
(オプション)
params
に渡されたデータにカスタム変換を適用するために使用できる関数です。これを指定しない場合、params
は new URLSearchParams()
に直接渡されます。一部の API 統合では、異なる配列型をサポートするために、query-string
ライブラリのようなものを使用する必要がある場合があります。
fetchFn
(オプション)
ウィンドウのデフォルトをオーバーライドする fetch 関数。isomorphic-fetch
や cross-fetch
を利用する必要がある SSR 環境で役立ちます。
timeout
(オプション)
リクエストがタイムアウトするまでの最大時間をミリ秒単位で表す数値。
isJsonContentType
(オプション)
Headers
オブジェクトを受け取り、FetchArgs
引数の body
フィールドを JSON.stringify()
で文字列化する必要があるかどうかを判断するコールバック。
デフォルトの実装では、content-type
ヘッダーを検査し、"application/json"
や "application/vnd.api+json"
のような値と一致します。
jsonContentType
(オプション)
明示的な content-type
ヘッダーがない json 化可能な body を持つリクエストに対して content-type
ヘッダーを自動的に設定するときに使用されます。デフォルトは "application/json"
です。
一般的な使用パターン
リクエストにデフォルトヘッダーを設定する
prepareHeaders
の最も一般的なユースケースは、API リクエストに authorization
ヘッダーを自動的に含めることです。
- TypeScript
- JavaScript
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { RootState } from './store'
const baseQuery = fetchBaseQuery({
baseUrl: '/',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token
// If we have a token set in state, let's assume that we should be passing it.
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
},
})
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
const baseQuery = fetchBaseQuery({
baseUrl: '/',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token
// If we have a token set in state, let's assume that we should be passing it.
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
},
})
個々のクエリオプション
リクエストごとに定義できる動作がさらにあります。query
フィールドは、RequestInit
インターフェースで利用可能なデフォルトの fetch
オプションと、これらの追加オプションを含むオブジェクトを返す場合があります。
- TypeScript
- JavaScript
interface FetchArgs extends RequestInit {
url: string
params?: Record<string, any>
body?: any
responseHandler?:
| 'json'
| 'text'
| `content-type`
| ((response: Response) => Promise<any>)
validateStatus?: (response: Response, body: any) => boolean
timeout?: number
}
const defaultValidateStatus = (response: Response) =>
response.status >= 200 && response.status <= 299
const defaultValidateStatus = (response) =>
response.status >= 200 && response.status <= 299
body を設定する
デフォルトでは、fetchBaseQuery
は、行うすべてのリクエストが json
であると想定しているため、そのような場合は、url
を設定し、必要に応じて body
オブジェクトを渡すだけです。他の実装では、Headers
を手動で設定してコンテンツタイプを指定できます。
json
// omitted
endpoints: (builder) => ({
updateUser: builder.query({
query: (user: Record<string, string>) => ({
url: `users`,
method: 'PUT',
body: user // Body is automatically converted to json with the correct headers
}),
}),
text
// omitted
endpoints: (builder) => ({
updateUser: builder.query({
query: (user: Record<string, string>) => ({
url: `users`,
method: 'PUT',
headers: {
'content-type': 'text/plain',
},
body: user
}),
}),
クエリ文字列を設定する
fetchBaseQuery
は、オブジェクトを new URLSearchParms()
に渡すことによって、オブジェクトをシリアル化されたクエリ文字列に変換する簡単なメカニズムを提供します。これがニーズに合わない場合は、2 つのオプションがあります。
- カスタム変換を適用するために、
paramsSerializer
オプションをfetchBaseQuery
に渡します。 - 独自のクエリ文字列を構築し、
url
に設定します。
// omitted
endpoints: (builder) => ({
updateUser: builder.query({
query: (user: Record<string, string>) => ({
url: `users`,
// Assuming no `paramsSerializer` is specified, the user object is automatically converted
// and produces a url like /api/users?first_name=test&last_name=example
params: user
}),
}),
レスポンスの解析
デフォルトでは、fetchBaseQuery
は、取得するすべての Response
が json
として解析されると想定しています。そうしたくない場合は、text
のような代替のレスポンスハンドラーを指定するか、生の Response
オブジェクトを受け入れるカスタム関数を使用して、動作をカスタマイズできます。—任意の Response
メソッド を使用できるようにします。
responseHandler
フィールドは次のいずれかになります。
- TypeScript
- JavaScript
type ResponseHandler =
| 'content-type'
| 'json'
| 'text'
| ((response: Response) => Promise<any>)
"json"
および "text"
値は、fetchBaseQuery
に、ボディを読み取るための対応するフェッチ応答メソッドを指示します。content-type
は、ヘッダーフィールドを最初にチェックして JSON のように見えるかどうかを判断し、その後、それらの 2 つのメソッドのいずれかを使用します。コールバックを使用すると、自分でボディを処理できます。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// This is the same as passing 'text'
responseHandler: (response) => response.text(),
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// This is the same as passing 'text'
responseHandler: (response) => response.text(),
}),
}),
}),
})
未定義のボディで 200
のみを返す API に json
リクエストを行う場合、fetchBaseQuery
はそれを undefined
としてパススルーし、json
として解析しようとしません。これは、一部の API、特に delete
リクエストでよくあります。
非標準のレスポンスステータスコードの処理
デフォルトでは、fetchBaseQuery
はステータスコードが 2xx
でないすべての Response
を reject
し、error
に設定します。これは、axios
やその他の一般的なライブラリで最も経験したことのある動作と同じです。非標準の API を扱っている場合は、validateStatus
オプションを使用してこの動作をカスタマイズできます。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: we have a backend API always returns a 200,
// but sets an `isError` property when there is an error.
validateStatus: (response, result) =>
response.status === 200 && !result.isError,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const customApi = createApi({
// Set the baseUrl for every endpoint below
baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: we have a backend API always returns a 200,
// but sets an `isError` property when there is an error.
validateStatus: (response, result) =>
response.status === 200 && !result.isError,
}),
}),
}),
})
リクエストにカスタムタイムアウトを追加する
デフォルトでは、fetchBaseQuery
にはデフォルトのタイムアウト値が設定されていません。これは、APIがリクエストを解決するか、ブラウザのデフォルトのタイムアウト(通常は5分)に達するまで、リクエストが保留状態になることを意味します。ほとんどの場合、これは望ましい動作ではありません。fetchBaseQuery
を使用する場合、baseQuery
または個々のエンドポイントで timeout
を設定することができます。両方のオプションを指定した場合、エンドポイントの値が優先されます。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const api = createApi({
// Set a default timeout of 10 seconds
baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: 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.
timeout: 1000,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
export const api = createApi({
// Set a default timeout of 10 seconds
baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => ({
url: `users`,
// Example: 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.
timeout: 1000,
}),
}),
}),
})