Logo

dev-resources.site

for different kinds of informations.

How to organize your API layer in React using React Query

Published at
2/19/2024
Categories
react
cleancode
api
reactquery
Author
veljkoza
Categories
4 categories in total
react
open
cleancode
open
api
open
reactquery
open
Author
8 person written this
veljkoza
open
How to organize your API layer in React using React Query

You are all probably using React Query (Tanstack Query) to help you with the data fetching in your React project. If you are not, what are you doing?

React Query is a server state management library for React. It expects that you provide a Promise returning function and it will handle the rest. You will get things like data caching out of the box plus a bunch of utility states like isLoading, error, isError etc which will make your life as a developer easier. Oh, did I say that you also get data caching out of the box?

Enough with React Query. Let's move on to see how we've usually dealt with organizing our API layer and how we can improve on it.

The Old Way of doing things:

1. You have your service file:

// api/posts.service.ts
export const getPosts = () => axios.get<Post[]>("http://localhost:5000/posts")
export const createPost = ({title, description}: CreatePostParams) => axios.post<Post>("http://localhost:5000/posts", {title, description})
Enter fullscreen mode Exit fullscreen mode

2. Then you create queries/mutation hooks:

import {
  UseQueryOptions,
  QueryKey,
  UseMutationOptions,
  MutationFunction,
  useQuery,
} from '@tanstack/react-query';

const useGetPosts = (
  options: UseQueryOptions<Post[], unknown, Post[], QueryKey>,
) => useQuery({ queryKey: ['posts'], queryFn: getPosts, options });

const useCreatePost = (
  options: UseMutationOptions<Post, unknown, CreatePostParams, unknown>,
) =>
  useMutation<Post, unknown, CreatePostParams, unknown>({
    mutationFn: serviceFn as MutationFunction<Post, CreatePostParams>,
    ...options,
  });
Enter fullscreen mode Exit fullscreen mode

3. And finally use it in your component:

const PostsManager = () => {
    const { data, isLoading } = useGetPosts() // data is fully typesafe
    const { mutate } = useCreatePost() // mutate function is fully typesafe

    const clickHandler = () => {
        mutate({title: "Post title", description: "Post description"})
    }
    return <div>...</div>
}
Enter fullscreen mode Exit fullscreen mode

Congratulations! If you are doing this you are on the right path.

But you can notice how things get tedious as our service layer expands.

There are a couple of problems with this approach:

  • We have to keep track of all of these types (params and response type)
  • We have to manually create each new query or mutation
  • Query keys are magic strings with no centralization

The Centralized way:

Wouldn’t it be an amazing developer experience if we could do something like this:

// service layer
const postsService = { getPosts, createPost, getPostById }

// queries layer
const postsQueries = createQueriesFromService(postsService, 'posts')

const PostsManager = () => {
    const { data, isLoading } = postsQueries.getPosts.useQuery() // fully typesafe data and options etc.
    const { mutate } = postQueries.createPost.useMutation() 

    const { data, isLoading } = postsQueries.getPostById.useQuery({id: "123"}) // fully typesafe params etc.

    const queryKey = postsQueries.getPostById.queryKey({id: "123"}) // automated query key handling + fully typesafe

    const clickHandler = () => {
        mutate({title: "Post title", description: "Post description"}) // fully typesafe mutate function
    }
    return <div>...</div>
}
Enter fullscreen mode Exit fullscreen mode

The service layer is neatly organized and is the single source of truth for our queries layer. There is no manual typing, no manual hook creation.

All of the types are magically inferred. You just need to pass the service object to createQueriesFromService function and that’s it.

"But where do you find this magical createQueriesFromService function?"

-- You probably

I extracted the code into a separate open source npm library creatively called react-query-factory for everyone to use.

If you want to contribute or check out the code here’s the GitHub repository.

🥷🏼 You’ll notice I shamelessly stole some concepts from tRPC, but you know what they say about great artists.

For bonus Developer Experience you can have one object which encapsulates all of the queries. For example:

// service layer
const postsService = { getPosts, createPost, getPostById };
const productService = { getProducts, addProductToCart, getProductById };

// queries layer
const postsQueries = createQueriesFromService(postsService, "posts");
const productsQueries = 
createQueriesFromService(productService, "products");

// centralized queries
const queries = { posts: postsQueries, products: productsQueries };

const Foo = () => {
    const { data } = queries.products.getProducts.useQuery()
    ...
}
Enter fullscreen mode Exit fullscreen mode

This way your API layer is centralized in queries object, so next developer doesn’t have to second guess names like productQueries, since they can rely on autocomplete to offer them suggestions by typing queries. and pressing cmd + space or ctrl + space.

That’s all folks, thanks for reading!

Linkedin: https://www.linkedin.com/in/sekulic-veljko/

reactquery Article's
30 articles in total
Favicon
Create an SSR Application with Vite, React, React Query and React Router
Favicon
TanStack query or RTK query.
Favicon
Simplified State Management with useRQState: A Better Alternative to useState
Favicon
Master React API Management with TanStack React Query: Best Practices & Examples
Favicon
How to Fetch Data Using Axios and React Query in ReactJS
Favicon
Efficient Refresh Token Implementation with React Query and Axios
Favicon
react-query swrjs alova In-Depth Comparison
Favicon
Mastering React Query. Simplifying Data Management in React with Separation Patterns
Favicon
Mastering React Query. Structuring Your Code for Scalability and Reusability
Favicon
Why you should try React Query?
Favicon
How to used Suspense?
Favicon
Infinite list loading 🤔, with React Query - useInfiniteQuery hook !
Favicon
Building a CRUD app with React Query, TypeScript, and Axios
Favicon
TLDR; Suspense in react-query
Favicon
TLDR; gcTime & staleTime in react-query
Favicon
Building Infinite Scrolling in React Js with React Query v5
Favicon
Optimized Infinite Scroll with Next.js 14 Server Actions and React Query
Favicon
Building a Todo List with TypeScript and React Query: A Comprehensive Guide
Favicon
React Safe Query - A lightweight, type-safe wrapper built around React Query
Favicon
React Query Mutations Offline React-Native
Favicon
Improving UX by using cache
Favicon
How to organize your API layer in React using React Query
Favicon
Conociendo a React Query
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Final]
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Part 2]
Favicon
Integrating React Query with Next.js 🚀
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Part 1]
Favicon
React query for data streaming
Favicon
Manual Fetch with Tanstack Query
Favicon
Setting up React Query in your React project

Featured ones: