RTK Query: How to modify query responses with transformResponse
Tags: react, redux
Published: 2023-02-12 08:14:08

This is my note when I learned how to customize raw API responses with the [transformResponse](https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#customizing-query-responses-with-transformresponse).

According to the official document,
>  Individual endpoints on [createApi](https://redux-toolkit.js.org/rtk-query/api/createApi) accept a [transformResponse](https://redux-toolkit.js.org/rtk-query/api/createApi) 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](https://redux-toolkit.js.org/rtk-query/usage/cache-behavior#default-cache-behavior) 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](https://redux-toolkit.js.org/rtk-query/api/createApi#basequery) return value (, and potentially meta and arg) from baseQuery and returns it unchanged without any manipulation
```typescript
    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
```
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](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
```typescript
    // from
    [
      {
        id: 1,
        name: Name,
        species: SpeciesName
        year: -2,
        ...
      },
      {
        ...
      },
      ...
    ]
    // to 
    {
      SpeciesName:[
        {
          id: 1,
          name: Name,
          species: SpeciesName,
          year: "2 BBY"
          ...
        },
        ...
      ],
      [
        ...
      ],
      ...
    }
```
### API slice

You will use the transformResponse property in the API slice.
```typescript
    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 };
          }
        })
      })
    });
```
### modifyRowCharacters

This code corresponds to the second process the above.
```typescript
    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;
    };
```
### categorizeCharacters

This code is for the third process.
```typescript
    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;
    };
```
Thank you for reading :) 
The entire code is available [here](https://codesandbox.io/s/elated-morning-62jvcs).
The original article is [here](https://medium.com/@lada496/rtk-query-how-to-modify-query-responses-with-transformresponse-18200f1f2484)