createApi#

createApi is the core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. It generates an "API slice" structure that contains Redux logic (and optionally React hooks) that encapsulate the data fetching and caching process for you.

Example: src/services/pokemon.ts
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query<Pokemon, string>({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi

Parameters#

createApi accepts a single configuration object parameter with the following options:

baseQuery(args: InternalQueryArgs, api: BaseQueryApi): any;
endpoints(build: EndpointBuilder<InternalQueryArgs, TagTypes>): Definitions;
tagTypes?: readonly TagTypes[];
reducerPath?: ReducerPath;
serializeQueryArgs?: SerializeQueryArgs<InternalQueryArgs>;
keepUnusedDataFor?: number; // value is in seconds
refetchOnMountOrArgChange?: boolean | number; // value is in seconds
refetchOnFocus?: boolean;
refetchOnReconnect?: boolean;

baseQuery#

The base query used by each endpoint if no queryFn option is specified. RTK Query exports a utility called fetchBaseQuery as a lightweight wrapper around fetch for common use-cases.

Simulating axios-like interceptors with a custom base query
import {
BaseQueryFn,
FetchArgs,
fetchBaseQuery,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import { setToken, 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) {
// try to get a new token
const refreshResult = await baseQuery('/refreshToken', api, extraOptions)
if (refreshResult.data) {
// store the new token
api.dispatch(setToken(refreshResult.data))
// retry the initial query
result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
}
return result
}

tagTypes#

An array of string tag type names. Specifying tag types is optional, but you should define them so that they can be used for caching and invalidation. When defining an tag type, you will be able to provide them with provides and invalidate them with invalidates when configuring endpoints.

reducerPath#

The reducerPath is a unique key that your service will be mounted to in your store. If you call createApi more than once in your application, you will need to provide a unique value each time. Defaults to 'api'.

apis.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const apiOne = createApi({
reducerPath: 'apiOne',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
// ...endpoints
}),
})
const apiTwo = createApi({
reducerPath: 'apiTwo',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
// ...endpoints
}),
})

serializeQueryArgs#

Accepts a custom function if you have a need to change the creation of cache keys for any reason.

Defaults to:

export const defaultSerializeQueryArgs: SerializeQueryArgs<any> = ({
endpoint,
queryArgs,
}) => {
// Sort the object keys before stringifying, to prevent useQuery({ a: 1, b: 2 }) having a different cache key than useQuery({ b: 2, a: 1 })
return `${endpoint}(${JSON.stringify(
queryArgs,
Object.keys(queryArgs || {}).sort()
)})`
}

endpoints#

Endpoints are just a set of operations that you want to perform against your server. You define them as an object using the builder syntax. There are two basic endpoint types: query and mutation.

Anatomy of an endpoint#

  • query (required)

    • query is the only required property, and can be a function that returns either a string or an object which is passed to your baseQuery. If you are using fetchBaseQuery, this can return either a string or an object of properties in FetchArgs. If you use your own custom baseQuery, you can customize this behavior to your liking
  • transformResponse (optional)

    • A function to manipulate the data returned by a query or mutation
    • Unpack a deeply nested collection
      transformResponse: (response) => response.some.nested.collection
    • Normalize the response data
      transformResponse: (response) =>
      response.reduce((acc, curr) => {
      acc[curr.id] = curr
      return acc
      }, {})
  • providesTags (optional)

    • Used by queries to provide tags to the cache.
    • Expects an array of tag type strings, an array of objects of tag types with ids, or a function that returns such an array.
      1. ['Post'] - equivalent to b
      2. [{ type: 'Post' }] - equivalent to a
      3. [{ type: 'Post', id: 1 }]
      4. (result, error, arg) => ['Post'] - equivalent to e
      5. (result, error, arg) => [{ type: 'Post' }] - equivalent to d
      6. (result, error, arg) => [{ type: 'Post', id: 1 }]
  • invalidatesTags (optional)

    • Used by mutations for cache invalidation purposes.
    • Expects the same shapes as providesTags.
  • onQuery (optional) - Available to both queries and mutations

    • Can be used in mutations for optimistic updates.
    • Mutation onQuery signature
      async function onQuery(
      arg: QueryArg,
      {
      dispatch,
      getState,
      extra,
      requestId,
      resultPromise,
      getCacheEntry,
      }: MutationLifecycleApi
      ): Promise<void>
    • Query onQuery signature
      async function onQuery(
      arg: QueryArg,
      {
      dispatch,
      getState,
      extra,
      requestId,
      resultPromise,
      getCacheEntry,
      updateCacheEntry,
      }: QueryLifecycleApi
      ): Promise<void>
  • onCacheEntryAdded (optional) - Available to both queries and mutations

    • Can be used for streaming updates.
    • Mutation onCacheEntryAdded signature
      async function onCacheEntryAdded(
      arg: QueryArg,
      {
      dispatch,
      getState,
      extra,
      requestId,
      cleanup,
      firstValueResolved,
      getCacheEntry,
      }: MutationCacheLifecycleApi
      ): Promise<void>
    • Query onCacheEntryAdded signature
      async function onCacheEntryAdded(
      arg: QueryArg,
      {
      dispatch,
      getState,
      extra,
      requestId,
      cleanup,
      firstValueResolved,
      getCacheEntry,
      updateCacheEntry,
      }: QueryCacheLifecycleApi
      ): Promise<void>

How endpoints get used#

When defining a key like getPosts as shown below, it's important to know that this name will become exportable from api and be able to referenced under api.endpoints.getPosts.useQuery(), api.endpoints.getPosts.initiate() and api.endpoints.getPosts.select(). The same thing applies to mutations but they reference useMutation instead of useQuery.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
provides: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : [],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `posts`,
method: 'POST',
body,
}),
invalidates: ['Posts'],
}),
}),
})
// Auto-generated hooks
export const { useGetPostsQuery, useAddPostMutation } = api
// Possible exports
export const { endpoints, reducerPath, reducer, middleware } = api
// reducerPath, reducer, middleware are only used in store configuration
// endpoints will have:
// endpoints.getPosts.initiate(), endpoints.getPosts.select(), endpoints.getPosts.useQuery()
// endpoints.addPost.initiate(), endpoints.addPost.select(), endpoints.addPost.useMutation()
// see `createApi` overview for _all exports_

Transforming the data returned by an endpoint before caching#

In some cases, you may want to manipulate the data returned from a query before you put it in the cache. In this instance, you can take advantage of transformResponse.

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

function defaultTransformResponse(baseQueryReturnValue: unknown) {
return baseQueryReturnValue
}

To change it, provide a function that looks like:

transformResponse: (response) => response.some.deeply.nested.property
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: (builder) => ({
getPosts: builder.query<Post[], void>({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response: { posts: { data: Post[] } }) =>
response.posts.data,
}),
}),
})

keepUnusedDataFor#

Defaults to 60 (this value is in seconds). This is how long RTK Query will keep your data cached for after the last component unsubscribes. For example, if you query an endpoint, then unmount the component, then mount another component that makes the same request within the given time frame, the most recent value will be served from the cache.

refetchOnMountOrArgChange#

Defaults to false. This setting allows you to control whether if a cached result is already available RTK Query will only serve a cached result, or if it should refetch when set to true or if an adequate amount of time has passed since the last successful query result.

  • false - Will not cause a query to be performed unless it does not exist yet.
  • true - Will always refetch when a new subscriber to a query is added. Behaves the same as calling the refetch callback or passing forceRefetch: true in the action creator.
  • number - Value is in seconds. If a number is provided and there is an existing query in the cache, it will compare the current time vs the last fulfilled timestamp, and only refetch if enough time has elapsed.

If you specify this option alongside skip: true, this will not be evaluated until skip is false.

note

You can set this globally in createApi, but you can also override the default value and have more granular control by passing refetchOnMountOrArgChange to each individual hook call or when dispatching the initiate action.

refetchOnFocus#

Defaults to false. This setting allows you to control whether RTK Query will try to refetch all subscribed queries after the application window regains focus.

If you specify this option alongside skip: true, this will not be evaluated until skip is false.

Note: requires setupListeners to have been called.

note

You can set this globally in createApi, but you can also override the default value and have more granular control by passing refetchOnFocus to each individual hook call or when dispatching the initiate action.

If you specify track: false when manually dispatching queries, RTK Query will not be able to automatically refetch for you.

refetchOnReconnect#

Defaults to false. This setting allows you to control whether RTK Query will try to refetch all subscribed queries after regaining a network connection.

If you specify this option alongside skip: true, this will not be evaluated until skip is false.

Note: requires setupListeners to have been called.

note

You can set this globally in createApi, but you can also override the default value and have more granular control by passing refetchOnReconnect to each individual hook call or when dispatching the initiate action.

If you specify track: false when manually dispatching queries, RTK Query will not be able to automatically refetch for you.

Return value#

See the "created Api" API reference