Skip to content
Go back

Using React Query with GraphQL for Optimized Fetching

Using React Query with GraphQL for Optimized Fetching

Introduction

Combine the power of React Query’s caching with GraphQL’s precise data fetching for optimal user experience and performance.

Prerequisites

Step 1: Install Dependencies

npm install @tanstack/react-query graphql-request graphql

Step 2: Configure React Query Client

Create lib/queryClient.ts:

import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5 minutes
      cacheTime: 10 * 60 * 1000, // 10 minutes
      retry: 3,
    },
  },
});

Step 3: Create GraphQL Client

Create lib/graphql.ts:

import { GraphQLClient } from "graphql-request";

const endpoint =
  process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || "http://localhost:4000/graphql";

export const graphqlClient = new GraphQLClient(endpoint, {
  headers: {
    authorization: `Bearer ${typeof window !== "undefined" ? localStorage.getItem("token") : ""}`,
  },
});

Step 4: Define GraphQL Queries

Create queries/users.ts:

import { gql } from "graphql-request";

export const GET_USERS = gql`
  query GetUsers($limit: Int, $offset: Int) {
    users(limit: $limit, offset: $offset) {
      id
      name
      email
      avatar
    }
  }
`;

export const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      avatar
      posts {
        id
        title
        content
      }
    }
  }
`;

export const CREATE_USER = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

Step 5: Create Custom Hooks

Create hooks/useUsers.ts:

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { graphqlClient } from "@/lib/graphql";
import { GET_USERS, GET_USER, CREATE_USER } from "@/queries/users";

export function useUsers(limit = 10, offset = 0) {
  return useQuery({
    queryKey: ["users", { limit, offset }],
    queryFn: () => graphqlClient.request(GET_USERS, { limit, offset }),
  });
}

export function useUser(id: string) {
  return useQuery({
    queryKey: ["user", id],
    queryFn: () => graphqlClient.request(GET_USER, { id }),
    enabled: !!id,
  });
}

export function useCreateUser() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (input: any) => graphqlClient.request(CREATE_USER, { input }),
    onSuccess: () => {
      // Invalidate and refetch users list
      queryClient.invalidateQueries({ queryKey: ["users"] }); 
    },
  });
}

Step 6: Setup Provider

Update app/layout.tsx:

'use client'

import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { queryClient } from '@/lib/queryClient';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <QueryClientProvider client={queryClient}>
          {children}
          <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
      </body>
    </html>
  );
}

Step 7: Use in Components

Create components/UsersList.tsx:

import { useUsers, useCreateUser } from '@/hooks/useUsers';
import { useState } from 'react';

export default function UsersList() {
  const [page, setPage] = useState(0);
  const { data, isLoading, error } = useUsers(10, page * 10);
  const createUser = useCreateUser();

  const handleCreateUser = () => {
    createUser.mutate({
      name: 'New User',
      email: 'newuser@example.com',
    });
  };

  if (isLoading) return <div>Loading users...</div>;
  if (error) return <div>Error loading users</div>;

  return (
    <div>
      <button
        onClick={handleCreateUser}
        disabled={createUser.isPending}
        className="mb-4 px-4 py-2 bg-blue-600 text-white rounded"
      >
        {createUser.isPending ? 'Creating...' : 'Create User'}
      </button>

      <div className="grid gap-4">
        {data?.users?.map((user: any) => (
          <div key={user.id} className="p-4 border rounded">
            <h3>{user.name}</h3>
            <p>{user.email}</p>
          </div>
        ))}
      </div>

      <div className="mt-4 space-x-2">
        <button
          onClick={() => setPage(Math.max(0, page - 1))}
          disabled={page === 0}
          className="px-4 py-2 bg-gray-200 rounded"
        >
          Previous
        </button>
        <button
          onClick={() => setPage(page + 1)}
          className="px-4 py-2 bg-gray-200 rounded"
        >
          Next
        </button>
      </div>
    </div>
  );
}

Summary

React Query with GraphQL provides powerful caching, background updates, and optimistic UI patterns while maintaining GraphQL’s flexible data fetching capabilities.


Share this post on:

Previous Post
Handling Infinite Scroll with Intersection Observer in React
Next Post
Creating a Design System with React + Tailwind