Search code examples
reactjstypescriptredux-toolkitrtk-query

How to fetch data from two related to each other endpoints Using RTK-Query


I have two endpoints:

GET /api/messages/:userId/latest and GET /api/messages/:userId/:page

When the application loads a screen then, it calls the first endpoint to get information which pages have to loaded for a specific users, for example:

{
   pages: [324, 325, 326]
}

and then, the second endpoint is called several times with parameters taken from the first endpoint.

My question is: How to define a query in Redux-Toolkit (RTK) Query which calls the endpoints in this way and then merges the results?

My code snippets:

export interface MessageApiParams {
  userId: String | undefined,
  page: number
}

export const baseQuery = fetchBaseQuery({
  baseUrl: '/api',
  prepareHeaders: (headers) => {
    const token = getAccessToken()?.token
    if (token) {
      headers.set("authorization", `Bearer ${token}`);
    }
    return headers;
  },
})

export const messagesApi = createApi({
  reducerPath: 'messagesApi',
  baseQuery: baseQuery,
  endpoints: builder => ({
    getMessages: builder.query<Messages, MessageApiParams>({
      query: (params: MessageApiParams) => {
        return ({
          url: `/users/${params.userId}/messages/${params.page}`,
          method: 'GET',
          headers: { Authorization: `Bearer ${getAccessToken()?.token}`}
        });
      }
    })
  })
})

Solution

  • You can combine multiple requests in a single custom query function.

    Example could look something like the following:

    export interface MessageApiParams {
      userId: String | undefined;
    }
    
    export interface MessagesApiResult {
      pages: number[];
      messages: MessageOrError[];
    }
    
    export const messagesApi = createApi({
      reducerPath: 'messagesApi',
      baseQuery,
      endpoints: builder => ({
        getMessages: builder.query<MessagesApiResult, MessageApiParams>({
          queryFn: async(
            { userId }: MessageApiParams,
            api,
            extraOptions,
            baseQuery
          ) => {
            // Fetch latest messages from endpoint #1 "/api/messages/:userId/latest"
            const messagesResult = await baseQuery({
              url: `/messages/${userId}/latest`,
              method: 'GET',
            });
    
            if (messagesResult.error) {
              return {
                error: messagesResult.error as /* your base query error type here */
              };
            }
    
            const pages = messagesResult as number[];
    
            // Fetch pages from endpoint #2 "/api/messages/:userId/:page"
            const pagesResults = await Promise.all(pages.map(
              page => baseQuery({
                url: `/messages/${userId}/${page}`,
                method: 'GET',
              })
            ));
    
            return {
              data: {
                pages,
                messages: pagesResults
              } as MessagesApiResult;
            };
          },
        }),
      }),
    });