A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://redux-toolkit.js.org/rtk-query/usage/customizing-queries below:

Customizing Queries | Redux Toolkit

Customizing queries

RTK Query is agnostic as to how your requests resolve. You can use any library you like to handle requests, or no library at all. RTK Query provides reasonable defaults expected to cover the majority of use cases, while also allowing room for customization to alter query handling to fit specific needs.

Customizing queries with baseQuery

The default method to handle queries is via the baseQuery option on createApi, in combination with the query option on an endpoint definition.

To process queries, endpoints are defined with a query option, which passes its return value to a common baseQuery function used for the API.

By default, RTK Query ships with fetchBaseQuery, which is a lightweight fetch wrapper that automatically handles request headers and response parsing in a manner similar to common libraries like axios. If fetchBaseQuery alone does not meet your needs, you can customize its behavior with a wrapper function, or create your own baseQuery function from scratch for createApi to use.

See also baseQuery API Reference.

Implementing a custom baseQuery

RTK Query expects a baseQuery function to be called with three arguments: args, api, and extraOptions. It is expected to return an object with either a data or error property, or a promise that resolves to return such an object.

tip

Base query and query functions must always catch errors themselves, and return it in an object!

function brokenCustomBaseQuery() {

const data = await fetchSomeData()
return { data }
}

function correctCustomBaseQuery() {

try {
const data = await fetchSomeData()
return { data }
} catch (error) {
return { error }
}
}
baseQuery function arguments

baseQuery example arguments

const customBaseQuery = (
args,
{ signal, dispatch, getState },
extraOptions,
) => {

}
baseQuery function return value
  1. Expected success result format

    return { data: YourData }
  2. Expected error result format

    return { error: YourError }

baseQuery example return value

const customBaseQuery = (
args,
{ signal, dispatch, getState },
extraOptions,
) => {
if (Math.random() > 0.5) return { error: 'Too high!' }
return { data: 'All good!' }
}

note

This format is required so that RTK Query can infer the return types for your responses.

At its core, a baseQuery function only needs to have the minimum return value to be valid; an object with a data or error property. It is up to the user to determine how they wish to use the provided arguments, and how requests are handled within the function itself.

fetchBaseQuery defaults

For fetchBaseQuery specifically, the return type is as follows:

Return types of fetchBaseQuery

Promise<
| {
data: any
error?: undefined
meta?: { request: Request; response: Response }
}
| {
error: {
status: number
data: any
}
data?: undefined
meta?: { request: Request; response: Response }
}
>
  1. Expected success result format with fetchBaseQuery

    return { data: YourData }
  2. Expected error result format with fetchBaseQuery

    return { error: { status: number, data: YourErrorData } }
Customizing query responses with transformResponse

Individual endpoints on createApi accept a transformResponse property which allows manipulation of the data returned by a query or mutation before it hits the cache.

transformResponse is called with the data that a successful baseQuery returns for the corresponding endpoint, and the return value of transformResponse is used as the cached data associated with that endpoint call.

By default, the payload from the server is returned directly.

function defaultTransformResponse(
baseQueryReturnValue: unknown,
meta: unknown,
arg: unknown,
) {
return baseQueryReturnValue
}
function defaultTransformResponse(baseQueryReturnValue, meta, arg) {
return baseQueryReturnValue
}

To change it, provide a function that looks like:

Unpack a deeply nested collection

transformResponse: (response, meta, arg) =>
response.some.deeply.nested.collection

transformResponse is called with the meta property returned from the baseQuery as its second argument, which can be used while determining the transformed response. The value for meta is dependent on the baseQuery used.

transformResponse meta example

transformResponse: (response: { sideA: Tracks; sideB: Tracks }, meta, arg) => {
if (meta?.coinFlip === 'heads') {
return response.sideA
}
return response.sideB
}

transformResponse is called with the arg property provided to the endpoint as its third argument, which can be used while determining the transformed response. The value for arg is dependent on the endpoint used, as well as the argument used when calling the query/mutation.

transformResponse arg example

transformResponse: (response: Posts, meta, arg) => {
return {
originalArg: arg,
data: response,
}
}

While there is less need to store the response in a normalized lookup table with RTK Query managing caching data, transformResponse can be leveraged to do so if desired.

Normalize the response data

transformResponse: (response) =>
response.reduce((acc, curr) => {
acc[curr.id] = curr
return acc
}, {})
















createEntityAdapter can also be used with transformResponse to normalize data, while also taking advantage of other features provided by createEntityAdapter, including providing an ids array, using sortComparer to maintain a consistently sorted list, as well as maintaining strong TypeScript support.

See also Websocket Chat API with a transformed response shape for an example of transformResponse normalizing response data in combination with createEntityAdapter, while also updating further data using streaming updates.

Customizing query responses with transformErrorResponse

Individual endpoints on createApi accept a transformErrorResponse property which allows manipulation of the error returned by a query or mutation before it hits the cache.

transformErrorResponse is called with the error that a failed baseQuery returns for the corresponding endpoint, and the return value of transformErrorResponse is used as the cached error associated with that endpoint call.

By default, the payload from the server is returned directly.

function defaultTransformResponse(
baseQueryReturnValue: unknown,
meta: unknown,
arg: unknown,
) {
return baseQueryReturnValue
}
function defaultTransformResponse(baseQueryReturnValue, meta, arg) {
return baseQueryReturnValue
}

To change it, provide a function that looks like:

Unpack a deeply nested error object

transformErrorResponse: (response, meta, arg) =>
response.data.some.deeply.nested.errorObject

transformErrorResponse is called with the meta property returned from the baseQuery as its second argument, which can be used while determining the transformed response. The value for meta is dependent on the baseQuery used.

transformErrorResponse meta example

transformErrorResponse: (
response: { data: { sideA: Tracks; sideB: Tracks } },
meta,
arg,
) => {
if (meta?.coinFlip === 'heads') {
return response.data.sideA
}
return response.data.sideB
}

transformErrorResponse is called with the arg property provided to the endpoint as its third argument, which can be used while determining the transformed response. The value for arg is dependent on the endpoint used, as well as the argument used when calling the query/mutation.

transformErrorResponse arg example

transformErrorResponse: (response: Posts, meta, arg) => {
return {
originalArg: arg,
error: response,
}
}
Customizing queries with queryFn

RTK Query comes with fetchBaseQuery out of the box, which makes it straightforward to define endpoints that talk to HTTP URLs (such as a typical REST API). We also have integrations with GraphQL as well. However, at its core, RTK Query is really about tracking loading state and cached values for any async request/response sequence, not just HTTP requests.

RTK Query supports defining endpoints that run arbitrary async logic and return a result. Individual endpoints on createApi accept a queryFn property, which let you write your own async function with whatever logic you want inside.

This can be useful for scenarios where you want to have particularly different behavior for a single endpoint, or where the query itself is not relevant, including:

See also queryFn API Reference for the type signature and available options.

Implementing a queryFn

A queryFn can be thought of as an inline baseQuery. It will be called with the same arguments as baseQuery, as well as the provided baseQuery function itself (arg, api, extraOptions, and baseQuery). Similarly to baseQuery, it is expected to return an object with either a data or error property, or a promise that resolves to return such an object.

Basic queryFn Example

Basic queryFn example

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { userAPI, User } from './userAPI'

const api = createApi({
baseQuery: fetchBaseQuery({ url: '/' }),
endpoints: (build) => ({

getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),

getUser: build.query<User, string>({
queryFn: async (userId: string) => {
try {
const user = await userApi.getUserById(userId)

return { data: user }
} catch (error) {

return { error }
}
},
}),
}),
})
queryFn function arguments

queryFn example arguments

const queryFn = (
args,
{ signal, dispatch, getState },
extraOptions,
baseQuery,
) => {

}
queryFn function return value
  1. Expected success result format

    return { data: YourData }
  2. Expected error result format

    return { error: YourError }

queryFn example return value

const queryFn = (
args,
{ signal, dispatch, getState },
extraOptions,
baseQuery,
) => {
if (Math.random() > 0.5) return { error: 'Too high!' }
return { data: 'All good!' }
}
Examples - baseQuery Axios baseQuery

This example implements a very basic axios-based baseQuery utility.

Basic axios baseQuery

import { createApi } from '@reduxjs/toolkit/query'
import type { BaseQueryFn } from '@reduxjs/toolkit/query'
import axios from 'axios'
import type { AxiosRequestConfig, AxiosError } from 'axios'

const axiosBaseQuery =
(
{ baseUrl }: { baseUrl: string } = { baseUrl: '' },
): BaseQueryFn<
{
url: string
method?: AxiosRequestConfig['method']
data?: AxiosRequestConfig['data']
params?: AxiosRequestConfig['params']
headers?: AxiosRequestConfig['headers']
},
unknown,
unknown
> =>
async ({ url, method, data, params, headers }) => {
try {
const result = await axios({
url: baseUrl + url,
method,
data,
params,
headers,
})
return { data: result.data }
} catch (axiosError) {
const err = axiosError as AxiosError
return {
error: {
status: err.response?.status,
data: err.response?.data || err.message,
},
}
}
}

const api = createApi({
baseQuery: axiosBaseQuery({
baseUrl: 'https://example.com',
}),
endpoints(build) {
return {
query: build.query({ query: () => ({ url: '/query', method: 'get' }) }),
mutation: build.mutation({
query: () => ({ url: '/mutation', method: 'post' }),
}),
}
},
})

Basic axios baseQuery

import { createApi } from '@reduxjs/toolkit/query'
import axios from 'axios'

const axiosBaseQuery =
({ baseUrl } = { baseUrl: '' }) =>
async ({ url, method, data, params, headers }) => {
try {
const result = await axios({
url: baseUrl + url,
method,
data,
params,
headers,
})
return { data: result.data }
} catch (axiosError) {
const err = axiosError
return {
error: {
status: err.response?.status,
data: err.response?.data || err.message,
},
}
}
}

const api = createApi({
baseQuery: axiosBaseQuery({
baseUrl: 'https://example.com',
}),
endpoints(build) {
return {
query: build.query({ query: () => ({ url: '/query', method: 'get' }) }),
mutation: build.mutation({
query: () => ({ url: '/mutation', method: 'post' }),
}),
}
},
})
GraphQL baseQuery

This example implements a very basic GraphQL-based baseQuery.

Basic GraphQL baseQuery

import { createApi } from '@reduxjs/toolkit/query'
import { request, gql, ClientError } from 'graphql-request'

const graphqlBaseQuery =
({ baseUrl }: { baseUrl: string }) =>
async ({ body }: { body: string }) => {
try {
const result = await request(baseUrl, body)
return { data: result }
} catch (error) {
if (error instanceof ClientError) {
return { error: { status: error.response.status, data: error } }
}
return { error: { status: 500, data: error } }
}
}

export const api = createApi({
baseQuery: graphqlBaseQuery({
baseUrl: 'https://graphqlzero.almansi.me/api',
}),
endpoints: (build) => ({
getPosts: build.query({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response) => response.posts.data,
}),
getPost: build.query({
query: (id) => ({
body: gql`
query {
post(id: ${id}) {
id
title
body
}
}
`,
}),
transformResponse: (response) => response.post,
}),
}),
})

Basic GraphQL baseQuery

import { createApi } from '@reduxjs/toolkit/query'
import { request, gql, ClientError } from 'graphql-request'

const graphqlBaseQuery =
({ baseUrl }) =>
async ({ body }) => {
try {
const result = await request(baseUrl, body)
return { data: result }
} catch (error) {
if (error instanceof ClientError) {
return { error: { status: error.response.status, data: error } }
}
return { error: { status: 500, data: error } }
}
}

export const api = createApi({
baseQuery: graphqlBaseQuery({
baseUrl: 'https://graphqlzero.almansi.me/api',
}),
endpoints: (build) => ({
getPosts: build.query({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response) => response.posts.data,
}),
getPost: build.query({
query: (id) => ({
body: gql`
query {
post(id: ${id}) {
id
title
body
}
}
`,
}),
transformResponse: (response) => response.post,
}),
}),
})

This example wraps fetchBaseQuery such that when encountering a 401 Unauthorized error, an additional request is sent to attempt to refresh an authorization token, and re-try to initial query after re-authorizing.

Simulating axios-like interceptors with a custom base query

import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'

const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {

const refreshResult = await baseQuery('/refreshToken', api, extraOptions)
if (refreshResult.data) {

api.dispatch(tokenReceived(refreshResult.data))

result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
}
return result
}

Simulating axios-like interceptors with a custom base query

import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'

const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {

const refreshResult = await baseQuery('/refreshToken', api, extraOptions)
if (refreshResult.data) {

api.dispatch(tokenReceived(refreshResult.data))

result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
}
return result
}
Preventing multiple unauthorized errors

Using async-mutex to prevent multiple calls to '/refreshToken' when multiple calls fail with 401 Unauthorized errors.

Preventing multiple calls to '/refreshToken'

import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'
import { Mutex } from 'async-mutex'


const mutex = new Mutex()
const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {

await mutex.waitForUnlock()
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {

if (!mutex.isLocked()) {
const release = await mutex.acquire()
try {
const refreshResult = await baseQuery(
'/refreshToken',
api,
extraOptions,
)
if (refreshResult.data) {
api.dispatch(tokenReceived(refreshResult.data))

result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
} finally {

release()
}
} else {

await mutex.waitForUnlock()
result = await baseQuery(args, api, extraOptions)
}
}
return result
}

Preventing multiple calls to '/refreshToken'

import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'
import { Mutex } from 'async-mutex'


const mutex = new Mutex()
const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth = async (args, api, extraOptions) => {

await mutex.waitForUnlock()
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {

if (!mutex.isLocked()) {
const release = await mutex.acquire()
try {
const refreshResult = await baseQuery(
'/refreshToken',
api,
extraOptions,
)
if (refreshResult.data) {
api.dispatch(tokenReceived(refreshResult.data))

result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
} finally {

release()
}
} else {

await mutex.waitForUnlock()
result = await baseQuery(args, api, extraOptions)
}
}
return result
}
Automatic retries

RTK Query exports a utility called retry that you can wrap the baseQuery in your API definition with. It defaults to 5 attempts with a basic exponential backoff.

The default behavior would retry at these intervals:

  1. 600ms * random(0.4, 1.4)
  2. 1200ms * random(0.4, 1.4)
  3. 2400ms * random(0.4, 1.4)
  4. 4800ms * random(0.4, 1.4)
  5. 9600ms * random(0.4, 1.4)

Retry every request 5 times by default

import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]


const staggeredBaseQuery = retry(fetchBaseQuery({ baseUrl: '/' }), {
maxRetries: 5,
})
export const api = createApi({
baseQuery: staggeredBaseQuery,
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
getPost: build.query<PostsResponse, string>({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 },
}),
}),
})

export const { useGetPostsQuery, useGetPostQuery } = api

Retry every request 5 times by default

import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'


const staggeredBaseQuery = retry(fetchBaseQuery({ baseUrl: '/' }), {
maxRetries: 5,
})
export const api = createApi({
baseQuery: staggeredBaseQuery,
endpoints: (build) => ({
getPosts: build.query({
query: () => ({ url: 'posts' }),
}),
getPost: build.query({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 },
}),
}),
})

export const { useGetPostsQuery, useGetPostQuery } = api

In the event that you didn't want to retry on a specific endpoint, you can just set maxRetries: 0.

info

It is possible for a hook to return data and error at the same time. By default, RTK Query will keep whatever the last 'good' result was in data until it can be updated or garbage collected.

Bailing out of error re-tries

The retry utility has a fail method property attached which can be used to bail out of retries immediately. This can be used for situations where it is known that additional re-tries would be guaranteed to all fail and would be redundant.

Bailing out of error re-tries

import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'
import type { FetchArgs } from '@reduxjs/toolkit/query'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]

const staggeredBaseQueryWithBailOut = retry(
async (args: string | FetchArgs, api, extraOptions) => {
const result = await fetchBaseQuery({ baseUrl: '/api/' })(
args,
api,
extraOptions,
)



if (result.error?.status === 401) {
retry.fail(result.error, result.meta)
}

return result
},
{
maxRetries: 5,
},
)

export const api = createApi({
baseQuery: staggeredBaseQueryWithBailOut,
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
getPost: build.query<Post, string>({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 },
}),
}),
})
export const { useGetPostsQuery, useGetPostQuery } = api

Bailing out of error re-tries

import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'

const staggeredBaseQueryWithBailOut = retry(
async (args, api, extraOptions) => {
const result = await fetchBaseQuery({ baseUrl: '/api/' })(
args,
api,
extraOptions,
)



if (result.error?.status === 401) {
retry.fail(result.error, result.meta)
}

return result
},
{
maxRetries: 5,
},
)

export const api = createApi({
baseQuery: staggeredBaseQueryWithBailOut,
endpoints: (build) => ({
getPosts: build.query({
query: () => ({ url: 'posts' }),
}),
getPost: build.query({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 },
}),
}),
})
export const { useGetPostsQuery, useGetPostQuery } = api
Adding Meta information to queries

A baseQuery can also include a meta property in its return value. This can be beneficial in cases where you may wish to include additional information associated with the request such as a request ID or timestamp.

In such a scenario, the return value would look like so:

  1. Expected success result format with meta

    return { data: YourData, meta: YourMeta }
  2. Expected error result format with meta

    return { error: YourError, meta: YourMeta }

baseQuery example with meta information

import { fetchBaseQuery, createApi } from '@reduxjs/toolkit/query'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'
import { uuid } from './idGenerator'

type Meta = {
requestId: string
timestamp: number
}

const metaBaseQuery: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError,
{},
Meta & FetchBaseQueryMeta
> = async (args, api, extraOptions) => {
const requestId = uuid()
const timestamp = Date.now()

const baseResult = await fetchBaseQuery({ baseUrl: '/' })(
args,
api,
extraOptions,
)

return {
...baseResult,
meta: baseResult.meta && { ...baseResult.meta, requestId, timestamp },
}
}

const DAY_MS = 24 * 60 * 60 * 1000

interface Post {
id: number
name: string
timestamp: number
}
type PostsResponse = Post[]

const api = createApi({
baseQuery: metaBaseQuery,
endpoints: (build) => ({


getRecentPosts: build.query<PostsResponse, void>({
query: () => 'posts',
transformResponse: (returnValue: PostsResponse, meta) => {



if (!meta) return []
return returnValue.filter(
(post) => post.timestamp >= meta.timestamp - DAY_MS,
)
},
}),
}),
})

baseQuery example with meta information

import { fetchBaseQuery, createApi } from '@reduxjs/toolkit/query'
import { uuid } from './idGenerator'

const metaBaseQuery = async (args, api, extraOptions) => {
const requestId = uuid()
const timestamp = Date.now()

const baseResult = await fetchBaseQuery({ baseUrl: '/' })(
args,
api,
extraOptions,
)

return {
...baseResult,
meta: baseResult.meta && { ...baseResult.meta, requestId, timestamp },
}
}

const DAY_MS = 24 * 60 * 60 * 1000

const api = createApi({
baseQuery: metaBaseQuery,
endpoints: (build) => ({


getRecentPosts: build.query({
query: () => 'posts',
transformResponse: (returnValue, meta) => {



if (!meta) return []
return returnValue.filter(
(post) => post.timestamp >= meta.timestamp - DAY_MS,
)
},
}),
}),
})
Constructing a Dynamic Base URL using Redux state

In some cases, you may wish to have a dynamically altered base url determined from a property in your Redux state. A baseQuery has access to a getState method that provides the current store state at the time it is called. This can be used to construct the desired url using a partial url string, and the appropriate data from your store state.

Dynamically generated Base URL example

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query/react'
import type { Post } from './types'
import { selectProjectId } from './projectSlice'
import type { RootState } from '../store'

const rawBaseQuery = fetchBaseQuery({
baseUrl: 'www.my-cool-site.com/',
})

const dynamicBaseQuery: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
const projectId = selectProjectId(api.getState() as RootState)

if (!projectId) {
return {
error: {
status: 400,
statusText: 'Bad Request',
data: 'No project ID received',
},
}
}

const urlEnd = typeof args === 'string' ? args : args.url

const adjustedUrl = `project/${projectId}/${urlEnd}`
const adjustedArgs =
typeof args === 'string' ? adjustedUrl : { ...args, url: adjustedUrl }

return rawBaseQuery(adjustedArgs, api, extraOptions)
}

export const api = createApi({
baseQuery: dynamicBaseQuery,
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
}),
}),
})

export const { useGetPostsQuery } = api





Dynamically generated Base URL example

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { selectProjectId } from './projectSlice'

const rawBaseQuery = fetchBaseQuery({
baseUrl: 'www.my-cool-site.com/',
})

const dynamicBaseQuery = async (args, api, extraOptions) => {
const projectId = selectProjectId(api.getState())

if (!projectId) {
return {
error: {
status: 400,
statusText: 'Bad Request',
data: 'No project ID received',
},
}
}

const urlEnd = typeof args === 'string' ? args : args.url

const adjustedUrl = `project/${projectId}/${urlEnd}`
const adjustedArgs =
typeof args === 'string' ? adjustedUrl : { ...args, url: adjustedUrl }

return rawBaseQuery(adjustedArgs, api, extraOptions)
}

export const api = createApi({
baseQuery: dynamicBaseQuery,
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
}),
}),
})

export const { useGetPostsQuery } = api





Examples - transformResponse Unpacking deeply nested GraphQL data

GraphQL transformation example

import { createApi } from '@reduxjs/toolkit/query'
import { graphqlBaseQuery, gql } from './graphqlBaseQuery'

interface Post {
id: number
title: string
}

export const api = createApi({
baseQuery: graphqlBaseQuery({
baseUrl: '/graphql',
}),
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response: { posts: { data: Post[] } }) =>
response.posts.data,
}),
}),
})

GraphQL transformation example

import { createApi } from '@reduxjs/toolkit/query'
import { graphqlBaseQuery, gql } from './graphqlBaseQuery'

export const api = createApi({
baseQuery: graphqlBaseQuery({
baseUrl: '/graphql',
}),
endpoints: (build) => ({
getPosts: build.query({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response) => response.posts.data,
}),
}),
})
Normalizing data with createEntityAdapter

In the example below, transformResponse is used in conjunction with createEntityAdapter to normalize the data before storing it in the cache.

For a response such as:

[
{ id: 1, name: 'Harry' },
{ id: 2, name: 'Ron' },
{ id: 3, name: 'Hermione' },
]

The normalized cache data will be stored as:

{
ids: [1, 3, 2],
entities: {
1: { id: 1, name: "Harry" },
2: { id: 2, name: "Ron" },
3: { id: 3, name: "Hermione" },
}
}
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { createEntityAdapter } from '@reduxjs/toolkit'
import type { EntityState } from '@reduxjs/toolkit'

export interface Post {
id: number
name: string
}

const postsAdapter = createEntityAdapter<Post>({
sortComparer: (a, b) => a.name.localeCompare(b.name),
})

export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<EntityState<Post, number>, void>({
query: () => `posts`,
transformResponse(response: Post[]) {
return postsAdapter.addMany(postsAdapter.getInitialState(), response)
},
}),
}),
})

export const { useGetPostsQuery } = api
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { createEntityAdapter } from '@reduxjs/toolkit'

const postsAdapter = createEntityAdapter({
sortComparer: (a, b) => a.name.localeCompare(b.name),
})

export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query({
query: () => `posts`,
transformResponse(response) {
return postsAdapter.addMany(postsAdapter.getInitialState(), response)
},
}),
}),
})

export const { useGetPostsQuery } = api
Examples - queryFn Using a Third-Party SDK

Many services like Firebase and Supabase provide their own SDK to make requests. You can use those SDK methods in a queryFn:

Basic Third-Party SDK

import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query/react'
import { supabase } from './supabaseApi'

export const supabaseApi = createApi({
reducerPath: 'supabaseApi',
baseQuery: fakeBaseQuery(),
endpoints: (build) => ({
getBlogs: build.query({
queryFn: async () => {

const { data, error } = await supabase.from('blogs').select()
if (error) {
return { error }
}
return { data }
},
}),
}),
})

You could also try creating a custom base query that uses the SDK, and define endpoints that pass method names or args into that base query.

Using a no-op queryFn

In certain scenarios, you may wish to have a query or mutation where sending a request or returning data is not relevant for the situation. Such a scenario would be to leverage the invalidatesTags property to force re-fetch specific tags that have been provided to the cache.

See also "providing errors to the cache" to see additional detail and an example for such a scenario to 'refetch errored queries'.

Using a no-op queryFn

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
providesTags: ['Post'],
}),

getUsers: build.query<User[], void>({
query: () => 'users',
providesTags: ['User'],
}),

refetchPostsAndUsers: build.mutation<null, void>({

queryFn: () => ({ data: null }),



invalidatesTags: ['Post', 'User'],
}),
}),
})

Using a no-op queryFn

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
providesTags: ['Post'],
}),

getUsers: build.query({
query: () => 'users',
providesTags: ['User'],
}),

refetchPostsAndUsers: build.mutation({

queryFn: () => ({ data: null }),



invalidatesTags: ['Post', 'User'],
}),
}),
})
Streaming data with no initial request

RTK Query provides the ability for an endpoint to send an initial request for data, followed up with recurring streaming updates that perform further updates to the cached data as the updates occur. However, the initial request is optional, and you may wish to use streaming updates without any initial request fired off.

In the example below, a queryFn is used to populate the cache data with an empty array, with no initial request sent. The array is later populated using streaming updates via the onCacheEntryAdded endpoint option, updating the cached data as it is received.

Streaming data with no initial request

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Message } from './types'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Message'],
endpoints: (build) => ({
streamMessages: build.query<Message[], void>({



queryFn: () => ({ data: [] }),
async onCacheEntryAdded(arg, { updateCachedData, cacheEntryRemoved }) {
const ws = new WebSocket('ws://localhost:8080')

ws.addEventListener('message', (event) => {
updateCachedData((draft) => {
draft.push(JSON.parse(event.data))
})
})
await cacheEntryRemoved
ws.close()
},
}),
}),
})

Streaming data with no initial request

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Message'],
endpoints: (build) => ({
streamMessages: build.query({



queryFn: () => ({ data: [] }),
async onCacheEntryAdded(arg, { updateCachedData, cacheEntryRemoved }) {
const ws = new WebSocket('ws://localhost:8080')

ws.addEventListener('message', (event) => {
updateCachedData((draft) => {
draft.push(JSON.parse(event.data))
})
})
await cacheEntryRemoved
ws.close()
},
}),
}),
})
Performing multiple requests with a single query

In the example below, a query is written to fetch all posts for a random user. This is done using a first request for a random user, followed by getting all posts for that user. Using queryFn allows the two requests to be included within a single query, avoiding having to chain that logic within component code.

Performing multiple requests with a single query

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
endpoints: (build) => ({
getRandomUserPosts: build.query<Post, void>({
async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {

const randomResult = await fetchWithBQ('users/random')
if (randomResult.error)
return { error: randomResult.error as FetchBaseQueryError }
const user = randomResult.data as User
const result = await fetchWithBQ(`user/${user.id}/posts`)
return result.data
? { data: result.data as Post }
: { error: result.error as FetchBaseQueryError }
},
}),
}),
})

Performing multiple requests with a single query

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
endpoints: (build) => ({
getRandomUserPosts: build.query({
async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {

const randomResult = await fetchWithBQ('users/random')
if (randomResult.error) return { error: randomResult.error }
const user = randomResult.data
const result = await fetchWithBQ(`user/${user.id}/posts`)
return result.data ? { data: result.data } : { error: result.error }
},
}),
}),
})

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4