Skip to main content

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. See Customizing Queries if fetchBaseQuery does not handle your requirements.

baseQuery function arguments#

  • args - The return value of the query function for a given endpoint
  • api - The BaseQueryApi object, containing signal, dispatch and getState properties
    • signal - An AbortSignal object that may be used to abort DOM requests and/or read whether the request is aborted.
    • dispatch - The store.dispatch method for the corresponding Redux store
    • getState - A function that may be called to access the current store state
  • extraOptions - The value of the optional extraOptions property provided for a given endpoint

baseQuery function signature#

Base Query signature
export type BaseQueryFn<
Args = any,
Result = unknown,
Error = unknown,
DefinitionExtraOptions = {},
Meta = {}
> = (
args: Args,
api: BaseQueryApi,
extraOptions: DefinitionExtraOptions
) => MaybePromise<QueryReturnValue<Result, Error, Meta>>
export interface BaseQueryApi {
signal: AbortSignal
dispatch: ThunkDispatch<any, any, any>
getState: () => unknown
}
export type QueryReturnValue<T = unknown, E = unknown, M = unknown> =
| {
error: E
data?: undefined
meta?: M
}
| {
error?: undefined
data: T
meta?: M
}
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ...endpoints
}),
})

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.

See Anatomy of an endpoint for details on individual properties.

Query endpoint definition#

Query endpoint definition
export type QueryDefinition<
QueryArg,
BaseQuery extends BaseQueryFn,
TagTypes extends string,
ResultType,
ReducerPath extends string = string
> = {
query(arg: QueryArg): BaseQueryArg<BaseQuery>
/* either `query` or `queryFn` can be present, but not both simultaneously */
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>
): MaybePromise<QueryReturnValue<ResultType, BaseQueryError<BaseQuery>>>
/* transformResponse only available with `query`, not `queryFn` */
transformResponse?(
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>
): ResultType | Promise<ResultType>
extraOptions?: BaseQueryExtraOptions<BaseQuery>
providesTags?: ResultDescription<
TagTypes,
ResultType,
QueryArg,
BaseQueryError<BaseQuery>
>
keepUnusedDataFor?: number
onQueryStarted?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
updateCachedData, // available for query endpoints only
}: QueryLifecycleApi
): Promise<void>
onCacheEntryAdded?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
updateCachedData, // available for query endpoints only
}: QueryCacheLifecycleApi
): Promise<void>
}

Mutation endpoint definition#

Mutation endpoint definition
export type MutationDefinition<
QueryArg,
BaseQuery extends BaseQueryFn,
TagTypes extends string,
ResultType,
ReducerPath extends string = string,
Context = Record<string, any>
> = {
query(arg: QueryArg): BaseQueryArg<BaseQuery>
/* either `query` or `queryFn` can be present, but not both simultaneously */
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>
): MaybePromise<QueryReturnValue<ResultType, BaseQueryError<BaseQuery>>>
/* transformResponse only available with `query`, not `queryFn` */
transformResponse?(
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>
): ResultType | Promise<ResultType>
extraOptions?: BaseQueryExtraOptions<BaseQuery>
invalidatesTags?: ResultDescription<TagTypes, ResultType, QueryArg>
onQueryStarted?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
}: MutationLifecycleApi
): Promise<void>
onCacheEntryAdded?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
}: MutationCacheLifecycleApi
): 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',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : [],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `posts`,
method: 'POST',
body,
}),
invalidatesTags: ['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_

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.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
// ...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.

By default, this function will take the query arguments, sort object keys where applicable, stringify the result, and concatenate it with the endpoint name. This creates a cache key based on the combination of arguments + endpoint name (ignoring object key order), such that calling any given endpoint with the same arguments will result in the same cache key.

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.

keepUnusedDataFor example
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
keepUnusedDataFor: 5,
}),
}),
})

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 similarly by passing forceRefetch: true 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.

Anatomy of an endpoint#

query#

(required if no queryFn provided)

query 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.

query example
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
}),
}),
})

queryFn#

(required if no query provided)

Can be used in place of query as an inline function that bypasses baseQuery completely for the endpoint.

Called with the same arguments as baseQuery, as well as the provided baseQuery function itself. It is expected to return an object with either a data or error property, or a promise that resolves to return such an object.

See also Customizing queries with queryFn.

queryFn signature
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>
): MaybePromise<
| {
error: BaseQueryError<BaseQuery>
data?: undefined
}
| {
error?: undefined
data: ResultType
}
>
export interface BaseQueryApi {
signal: AbortSignal
dispatch: ThunkDispatch<any, any, any>
getState: () => unknown
}

queryFn function arguments#

  • args - The argument provided when the query itself is called
  • api - The BaseQueryApi object, containing signal, dispatch and getState properties
    • signal - An AbortSignal object that may be used to abort DOM requests and/or read whether the request is aborted.
    • dispatch - The store.dispatch method for the corresponding Redux store
    • getState - A function that may be called to access the current store state
  • extraOptions - The value of the optional extraOptions property provided for the endpoint
  • baseQuery - The baseQuery function provided to the api itself
Basic queryFn example
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
}),
flipCoin: build.query<'heads' | 'tails', void>({
queryFn(arg, queryApi, extraOptions, baseQuery) {
const randomVal = Math.random()
if (randomVal < 0.45) {
return { data: 'heads' }
}
if (randomVal < 0.9) {
return { data: 'tails' }
}
return { error: { status: 500, data: "Coin landed on it's edge!" } }
},
}),
}),
})

transformResponse#

(optional, not applicable with queryFn)

A function to manipulate the data returned by a query or mutation.

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.

See also Customizing query responses with transformResponse

Unpack a deeply nested collection
transformResponse: (response) => response.some.deeply.nested.collection

extraOptions#

(optional)

Passed as the third argument to the supplied baseQuery function

providesTags#

(optional, only for query endpoints)

Used by query endpoints. Determines which 'tag' is attached to the cached data returned by the query. 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 2
  2. [{ type: 'Post' }] - equivalent to 1
  3. [{ type: 'Post', id: 1 }]
  4. (result, error, arg) => ['Post'] - equivalent to 5
  5. (result, error, arg) => [{ type: 'Post' }] - equivalent to 4
  6. (result, error, arg) => [{ type: 'Post', id: 1 }]

See also Providing cache data.

providesTags example
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',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts' as const, id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
}),
})

invalidatesTags#

(optional, only for mutation endpoints)

Used by mutation endpoints. Determines which cached data should be either re-fetched or removed from the cache. Expects the same shapes as providesTags.

See also Invalidating cache data.

invalidatesTags example
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',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts' as const, id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
addPost: build.mutation<Post, Partial<Post>>({
query(body) {
return {
url: `posts`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
}),
}),
})

keepUnusedDataFor#

(optional, only for query endpoints)

Overrides the api-wide definition of keepUnusedDataFor for this endpoint only.

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.

keepUnusedDataFor example
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
keepUnusedDataFor: 5,
}),
}),
})

onQueryStarted#

(optional)

Available to both queries and mutations.

A function that is called when you start each individual query or mutation. The function is called with a lifecycle api object containing properties such as queryFulfilled, allowing code to be run when a query is started, when it succeeds, and when it fails (i.e. throughout the lifecycle of an individual query/mutation call).

Can be used in mutations for optimistic updates.

Lifecycle API properties#

  • dispatch - The dispatch method for the store.
  • getState - A method to get the current state for the store.
  • extra - extra as provided as thunk.extraArgument to the configureStore getDefaultMiddleware option.
  • requestId - A unique ID generated for the query/mutation.
  • queryFulfilled - A Promise that will resolve with the (transformed) query result. If the query fails, this Promise will reject with the error. This allows you to await for the query to finish.
  • getCacheEntry - A function that gets the current value of the cache entry.
  • updateCachedData (query endpoints only) - A function that accepts a 'recipe' callback specifying how to update the data for the corresponding cache at the time it is called. This uses immer internally, and updates can be written 'mutably' while safely producing the next immutable state.
Mutation onQueryStarted signature
async function onQueryStarted(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
}: MutationLifecycleApi
): Promise<void>
Query onQueryStarted signature
async function onQueryStarted(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
updateCachedData, // available for query endpoints only
}: QueryLifecycleApi
): Promise<void>
onQueryStarted query lifecycle example
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { messageCreated } from './notificationsSlice'
export interface Post {
id: number
name: string
}
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
endpoints: (build) => ({
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
async onQueryStarted(id, { dispatch, queryFulfilled }) {
// `onStart` side-effect
dispatch(messageCreated('Fetching post...'))
try {
const { data } = await queryFulfilled
// `onSuccess` side-effect
dispatch(messageCreated('Post received!'))
} catch (err) {
// `onError` side-effect
dispatch(messageCreated('Error fetching post!'))
}
},
}),
}),
})

onCacheEntryAdded#

(optional)

Available to both queries and mutations.

A function that is called when a new cache entry is added, i.e. when a new subscription for the endpoint + query parameters combination is created. The function is called with a lifecycle api object containing properties such as cacheDataLoaded & cacheDataRemoved, allowing code to be run when a cache entry is added, when cache data is loaded, and when the cache entry is removed (i.e. throughout the lifecycle of a cache entry).

Can be used for streaming updates.

Cache Lifecycle API properties#

  • dispatch - The dispatch method for the store.
  • getState - A method to get the current state for the store.
  • extra - extra as provided as thunk.extraArgument to the configureStore getDefaultMiddleware option.
  • requestId - A unique ID generated for the cache entry.
  • cacheEntryRemoved - A Promise that allows you to wait for the point in time when the cache entry has been removed from the cache, by not being used/subscribed to any more in the application for too long or by dispatching api.util.resetApiState.
  • cacheDataLoaded - A Promise that will resolve with the first value for this cache key. This allows you to await until an actual value is in the cache.
    Note: If the cache entry is removed from the cache before any value has ever been resolved, this Promise will reject with new Error('Promise never resolved before cacheEntryRemoved.') to prevent memory leaks. You can just re-throw that error (or not handle it at all) - it will be caught outside of cacheEntryAdded.
  • getCacheEntry - A function that gets the current value of the cache entry.
  • updateCachedData (query endpoints only) - A function that accepts a 'recipe' callback specifying how to update the data at the time it is called. This uses immer internally, and updates can be written 'mutably' while safely producing the next immutable state.
Mutation onCacheEntryAdded signature
async function onCacheEntryAdded(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
}: MutationCacheLifecycleApi
): Promise<void>
Query onCacheEntryAdded signature
async function onCacheEntryAdded(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
updateCachedData, // available for query endpoints only
}: QueryCacheLifecycleApi
): Promise<void>

Return value#

See the "created Api" API reference