Home > Software design >  How to implement multiple api call in a single query with RTK query
How to implement multiple api call in a single query with RTK query

Time:04-30

I'm new in RTK Query and I'm struggling with a use case I have to implement.

Scenario: I have to merge the results from two API calls: the first API call is a private API call while the second one is a public API call. I need to merge the responses from these two APIs and write the computed result into the RTK cache so the UI can update accordingly.

Problem: I'seeing that as soon as the await queryFullfilled is invoked, RTK Query immediately write into its cache the response from that API call and then when I make my calculation and try to update the RTK cache with apiSlice.util.updateQueryData the cache will change again. That's means that the UI will render twice, the first time using a wrong value (an array of persons) and the second time with the correct value (the JSON composed by ids and entities).

Question: Is there a way to have just 1 write into the RTK cache so I can have just the computed value I need ? Because what is happening is that for some instances I'm having into the cache an array while I need the {ids: [...], entities: {}} JSON.

import { createEntityAdapter } from '@reduxjs/toolkit';
import axios from 'axios';

export const personsAdapter = createEntityAdapter();
const permitsInitialState = personsAdapter.getInitialState();

export const apiSlice = myServiceApi.injectEndpoints({
  endpoints: (builder) => ({
    getPersons: builder.query({
      query: () => ({ url: '/persons', method: 'get' }),
      onQueryStarted: async (_, { dispatch, queryFulfilled }) => {
        try {
          // Resolving the private API call
          const { data: persons } = await queryFulfilled;

          // Just a random public API call
          const { data: todos } = await axios('https://jsonplaceholder.typicode.com/todos');

          const enhancedPersons = /** Here the logic that merge the todos and the persons */

          const state = personsAdapter.setAll(permitsInitialState, enhancedPermits);

          dispatch(
            apiSlice.util.updateQueryData('getPersons', _, (draft) => {
              Object.assign(draft, state);
            })
          );
        } catch (e) {
          console.error(e);
        }
      },
    }),
  }),
});

CodePudding user response:

That is one of the use cases of queryFn: Performing multiple requests with a single query

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

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
  endpoints: (build) => ({
    getRandomUserPosts: build.query<Post, void>({
      async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {
        // get a random user
        const randomResult = await fetchWithBQ('users/random')
        if (randomResult.error) throw randomResult.error
        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 }
      },
    }),
  }),
})
  • Related