GraphQL with React and Apollo Client

Apollo Client is the most widely used library for connecting a React application to a GraphQL API. It handles sending queries, caching results, updating the UI when data changes, and managing loading and error states — all with minimal boilerplate.

Installation

  npm install @apollo/client graphql

Setting Up the Apollo Provider

Wrap your entire React app in an ApolloProvider. This makes the Apollo Client instance available to every component in the tree.

  // main.jsx (entry point)
  import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';

  const client = new ApolloClient({
    uri:   'http://localhost:4000/graphql',   ← Your GraphQL server URL
    cache: new InMemoryCache(),              ← Built-in normalized cache
  });

  ReactDOM.createRoot(document.getElementById('root')).render(
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  );

Fetching Data with useQuery

useQuery runs a query when the component mounts and returns loading, error, and data states. React re-renders automatically when the data arrives.

  import { useQuery, gql } from '@apollo/client';

  const GET_PRODUCTS = gql`
    query GetProducts {
      products {
        id
        name
        price
      }
    }
  `;

  function ProductList() {
    const { loading, error, data } = useQuery(GET_PRODUCTS);

    if (loading) return <p>Loading...</p>;
    if (error)   return <p>Error: {error.message}</p>;

    return (
      <ul>
        {data.products.map(product => (
          <li key={product.id}>
            {product.name} — ${product.price}
          </li>
        ))}
      </ul>
    );
  }

Querying with Variables

  const GET_PRODUCT = gql`
    query GetProduct($id: ID!) {
      product(id: $id) {
        name
        price
        description
      }
    }
  `;

  function ProductDetail({ productId }) {
    const { loading, error, data } = useQuery(GET_PRODUCT, {
      variables: { id: productId },
    });

    if (loading) return <p>Loading...</p>;
    if (error)   return <p>Error loading product</p>;

    const { name, price, description } = data.product;
    return (
      <div>
        <h2>{name}</h2>
        <p>${price}</p>
        <p>{description}</p>
      </div>
    );
  }

Writing Data with useMutation

useMutation returns a function you call when the user triggers an action. It also returns loading and error states for the mutation.

  import { useMutation, gql } from '@apollo/client';

  const ADD_TO_CART = gql`
    mutation AddToCart($productId: ID!) {
      addToCart(productId: $productId) {
        id
        items { product { name } quantity }
      }
    }
  `;

  function AddToCartButton({ productId }) {
    const [addToCart, { loading, error }] = useMutation(ADD_TO_CART);

    return (
      <>
        <button
          onClick={() => addToCart({ variables: { productId } })}
          disabled={loading}
        >
          {loading ? 'Adding...' : 'Add to Cart'}
        </button>
        {error && <p>Failed to add: {error.message}</p>}
      </>
    );
  }

Automatic Cache Updates

Apollo Client caches query results by default. When a mutation changes data, you can update the cache so the UI reflects the change instantly without a full page refresh.

  const [createPost] = useMutation(CREATE_POST, {
    update(cache, { data: { createPost } }) {
      cache.modify({
        fields: {
          posts(existingPosts = []) {
            const newRef = cache.writeFragment({
              data: createPost,
              fragment: gql`fragment NewPost on Post { id title }`
            });
            return [...existingPosts, newRef];
          }
        }
      });
    }
  });

Key Points

  • Wrap your app in ApolloProvider with an ApolloClient instance to make GraphQL available everywhere.
  • useQuery fetches data on mount and returns loading, error, and data.
  • useMutation returns a function to trigger the mutation and tracks loading/error states.
  • Apollo Client caches query results automatically; mutations can update the cache to keep the UI in sync.
  • The gql template literal tag parses query strings into the AST format Apollo needs.

Leave a Comment