DEV Community

Cover image for RTK Query: How to modify query responses with transformResponse
Yuko
Yuko

Posted on

RTK Query: How to modify query responses with transformResponse

This is my note when I learned how to customize raw API responses with the transformResponse.

According to the official document,

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.

FYI: RTK Query has a feature for managing cached data that saves the data in the Redux store as a cache for reuse when another request is made for the same data.

transformResponse

By default, transformResponse receives a baseQuery return value (, and potentially meta and arg) from baseQuery and returns it unchanged without any manipulation

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

    ref: https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#customizing-query-responses-with-transformresponse
Enter fullscreen mode Exit fullscreen mode

You can modify the baseQuery return value within the transformResponse function.

Code Example

Here is my code example:
1) Retrieve Star Wars characters data from the following URL: https://akabab.github.io/starwars-api/api
2) Clean up unnecessary data, add a label of either “ABY” or “BBY” for the year information, and include Body Mass Index (BMI) data.
3) Change the data structure

    // from
    [
      {
        id: 1,
        name: Name,
        species: SpeciesName
        year: -2,
        ...
      },
      {
        ...
      },
      ...
    ]
    // to 
    {
      SpeciesName:[
        {
          id: 1,
          name: Name,
          species: SpeciesName,
          year: "2 BBY"
          ...
        },
        ...
      ],
      [
        ...
      ],
      ...
    }
Enter fullscreen mode Exit fullscreen mode

API slice

You will use the transformResponse property in the API slice.

    import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
    import {
      RowCharacter,
      CategorizedCharacters,
      ModifiedCharacter
    } from "./starWars-types";
    import { modifyRowCharacters, categorizeCharacters } from "./starWars-utils";

    export const starWarsApi = createApi({
      reducerPath: "starWarsApi",
      baseQuery: fetchBaseQuery({
        baseUrl: "https://akabab.github.io/starwars-api/api/"
      }),
      endpoints: (builder) => ({
        getCharacters: builder.query<CategorizedCharacters, undefined>({
          query: () => "all.json",
          transformResponse: (response: RowCharacter[]): CategorizedCharacters => {
            if (response.length === 0) return {};
            const modifiedCharacters = modifyRowCharacters(response);
            const categorizedCharacters = categorizeCharacters(modifiedCharacters);
            return { all: modifiedCharacters, ...categorizedCharacters };
          }
        })
      })
    });
Enter fullscreen mode Exit fullscreen mode

modifyRowCharacters

This code corresponds to the second process the above.

    export const modifyRowCharacters = (
      characters: RowCharacter[]
    ): ModifiedCharacter[] => {
      const modifiedCharacters: ModifiedCharacter[] = [];
      const calcBMI = (height: number, mass: number): number =>
        +(mass / Math.pow(height, 2)).toFixed(2);
      const convertToStarWarsDating = (year: number) =>
        year > 0 ? `${year} ABY` : `${Math.abs(year)} BBY`;
      for (const character of characters) {
        const bmi =
          character.height && character.mass
            ? calcBMI(character.height, character.mass)
            : undefined;
        modifiedCharacters.push({
          id: character.id,
          name: character.name,
          image: character.image,
          gender: character.gender,
          species: character.species,
          ...(bmi && {
            bmi,
            height: character.height,
            mass: character.mass
          }),
          ...(character.homeworld && { homeworld: character.homeworld }),
          ...(character.born && { born: convertToStarWarsDating(+character.born) }),
          ...(character.died && { died: convertToStarWarsDating(+character.died) })
        });
      }
      return modifiedCharacters;
    };
Enter fullscreen mode Exit fullscreen mode

categorizeCharacters

This code is for the third process.

    export const categorizeCharacters = (
      characters: ModifiedCharacter[]
    ): CategorizedCharacters => {
      const categorizedCharacters = characters.reduce(
        (acc: CategorizedCharacters, character: ModifiedCharacter) => {
          if (acc[character.species]) {
            acc[character.species].push(character);
            return acc;
          }
          acc[character.species] = [character];
          return acc;
        },
        {}
      );
      return categorizedCharacters;
    };
Enter fullscreen mode Exit fullscreen mode

Thank you for reading :)
The entire code is available here.
The original article is here

Top comments (0)