メインコンテンツにスキップ

fetchBaseQuery

これは、HTTP リクエストを簡略化することを目的とした、fetch を非常に小さくラップしたものです。これは、axiossuperagent、または他のよりヘビー級なライブラリの完全な代替品ではありませんが、HTTP リクエストのニーズの大部分をカバーします。

fetchBaseQuery は、RTK Query の baseQuery 設定オプションと互換性のあるデータフェッチメソッドを生成するファクトリー関数です。fetch の RequestInit インターフェースのすべての標準オプションに加えて、baseUrlprepareHeaders 関数、オプションの fetch 関数、paramsSerializer 関数、および timeout を取ります。

基本的な使い方

これを使用するには、API サービス定義を作成するときにインポートし、fetchBaseQuery(options) として呼び出し、結果を createApibaseQuery フィールドとして渡します。

src/services/pokemon.ts
// 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,
}),
}),
}),
})

シグネチャ

fetchBaseQuery のシグネチャ
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 ストアにアクセスできます。さらに、extraendpointtype、および forced にアクセスして、より詳細な条件付き動作をアンロックできます。

headers 引数を直接変更でき、それを返すのはオプションです。

prepareHeaders のシグネチャ
type prepareHeaders = (
headers: Headers,
api: {
getState: () => unknown
extra: unknown
endpoint: string
type: 'query' | 'mutation'
forced: boolean | undefined
},
) => Headers | void

paramsSerializer

(オプション)

params に渡されたデータにカスタム変換を適用するために使用できる関数です。これを指定しない場合、paramsnew URLSearchParams() に直接渡されます。一部の API 統合では、異なる配列型をサポートするために、query-string ライブラリのようなものを使用する必要がある場合があります。

fetchFn

(オプション)

ウィンドウのデフォルトをオーバーライドする fetch 関数。isomorphic-fetchcross-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 ヘッダーを自動的に含めることです。

"設定
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
},
})

個々のクエリオプション

リクエストごとに定義できる動作がさらにあります。query フィールドは、RequestInit インターフェースで利用可能なデフォルトの fetch オプションと、これらの追加オプションを含むオブジェクトを返す場合があります。

エンドポイントリクエストオプション
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

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 つのオプションがあります。

  1. カスタム変換を適用するために、paramsSerializer オプションを fetchBaseQuery に渡します。
  2. 独自のクエリ文字列を構築し、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 は、取得するすべての Responsejson として解析されると想定しています。そうしたくない場合は、text のような代替のレスポンスハンドラーを指定するか、生の Response オブジェクトを受け入れるカスタム関数を使用して、動作をカスタマイズできます。任意の Response メソッド を使用できるようにします。

responseHandler フィールドは次のいずれかになります。

type ResponseHandler =
| 'content-type'
| 'json'
| 'text'
| ((response: Response) => Promise<any>)

"json" および "text" 値は、fetchBaseQuery に、ボディを読み取るための対応するフェッチ応答メソッドを指示します。content-type は、ヘッダーフィールドを最初にチェックして JSON のように見えるかどうかを判断し、その後、それらの 2 つのメソッドのいずれかを使用します。コールバックを使用すると、自分でボディを処理できます。

レスポンスをテキストとして解析する
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 でないすべての Responsereject し、error に設定します。これは、axios やその他の一般的なライブラリで最も経験したことのある動作と同じです。非標準の API を扱っている場合は、validateStatus オプションを使用してこの動作をカスタマイズできます。

カスタム validateStatus の使用
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 を設定することができます。両方のオプションを指定した場合、エンドポイントの値が優先されます。

タイムアウト値の設定
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,
}),
}),
}),
})