Mastering API Data Transformation with React Query

Photo by Luca Bravo on Unsplash

Mastering API Data Transformation with React Query

Introduction

When it comes to dealing with the back-end (API endpoints), it often seems like they're throwing a party and not even considering the needs of the "client" (that's your front-end, by the way). They send data back, but it's not always in the right shape.

So, here's the deal: When you're interacting with an API in your front-end, sometimes you've got to give that data a little massage (you know, shape it up) to better fulfill your application needs.

Now, here's where things get interesting - and maybe a bit controversial. There are a few ways you can go about this, and they're all valid, but they come with their own quirks and trade-offs.

Transforming in the request

When I started using earlier versions of react-query in production, data transformation was a common requirement. At that time, the request was the place of choice for the transformation to happen:

// update this example
const fromAPI = (posts) => posts.map(data.title.toUpperCase());

const getPosts = () => axios.get('/posts').then(fromAPI);

const usePosts = () => 
  useQuery({ queryKey: ['posts'], queryFn: getPosts });

This approach offers readability and simplicity, which is perfectly fine if it is expected to consume the data anywhere in your application in this particular structure.

However, this leads us to the first drawback: the loss of the original data structure.

Given that this technique alters the data before it reaches the React Query cache, it becomes impossible to retrieve the data in its original, unmodified form.

But don’t worry there is a workaround to this predicament.

Transforming in useQuery

As I mentioned earlier, I used this approach on earlier versions of the library. However, on v3 a built-in solution for data transformation was introduced. Enter the select option. Here's how the documentation describes it:

This option can be used to transform or select a part of the data returned by the query function.

Now, you have the option to use a selector function to transform the data coming from the back-end:

const fromAPI = (posts) => posts.map(data.name.upperCase());

const usePosts = (select) => 
  useQuery({ 
    queryKey: ['posts'], 
    queryFn: getPosts,
    select,
  });

// in your component
const apiPosts = usePosts();
const posts = usePosts(fromApi);

Since using select is optional and it’s only called if the data is present, you can call the usePosts hook with and without the selector function. This allows you to easily access both the raw server data and the transformed data.

Pro Tip: To optimize performance and prevent the select function from running with every render, consider isolating it into its dedicated function, as demonstrated in the earlier example, or using a memoization technique like useCallback.

There's a lot more to explore about selectors and the cool ways to leverage them in React Query. I want to learn more you can read Optimizing API Data Usage in React Query with Selectors.
I hope you've found this exploration of API data transformations enjoyable. Until the next post! 🙂