The Ultimate Guide to Fetching GraphQL Data in React

GraphQL has taken the web development world by storm since publicly launching in 2015. This open-source data query and manipulation language provides a powerful, flexible alternative to the REST architecture that has dominated web APIs for the past two decades.

A key benefit of GraphQL is enabling clients to specify the exact data they need from an API. Instead of relying on rigid server-defined endpoints, a GraphQL server exposes a schema of its full data model and capabilities. Clients can send queries to a single endpoint, specifying what data to fetch or mutate based on that schema.

This approach offers several major advantages over REST APIs:

  • Avoids over-fetching and under-fetching data. With REST, endpoints return fixed data structures, so clients often must request more data than they need or make multiple requests to fetch all requirements. GraphQL queries always return exactly what‘s requested.
  • Fewer round trips. Applications can often fetch all required data for a view with a single query instead of making multiple calls to different REST endpoints.
  • Strong typing. GraphQL APIs are built around strict schemas using a type system to define possible data. This enables powerful tooling and validation for development.
  • Faster iteration. As frontend requirements change, developers can easily adjust their queries without needing to modify or create new API endpoints.

Because of these strengths, GraphQL has been rapidly adopted by major tech companies like Facebook, Twitter, Airbnb, and Shopify as well as countless smaller organizations. The 2020 State of JS survey found that 87.4% of developers who have used GraphQL plan to use it again [^1].

GraphQL in the React Ecosystem

React, Facebook‘s hugely popular JavaScript library for building user interfaces, is often associated with GraphQL. GraphQL was developed internally at Facebook before React was publicly launched, and the two technologies are highly complementary.

In particular, GraphQL enables client-driven data fetching approaches that align well with the component-centric design of React applications. Each React component declares its own data requirements as a GraphQL query and receives exactly the data it needs.

There are several excellent options for integrating GraphQL into React applications. Let‘s take an in-depth look at the most popular libraries and approaches.

Apollo Client

Apollo Client is the most widely used GraphQL client for React and other JavaScript applications. Launched in 2016, it‘s a fully-featured, opinionated platform for using GraphQL on the frontend.

Some of the key features Apollo Client provides include:

  • Declarative data fetching and state management via React hooks like useQuery and useMutation
  • Automatic caching of GraphQL query responses
  • Optimistic UI updates while awaiting server responses
  • Prefetching and lazy loading of query results
  • Polling and real-time subscriptions for live updates
  • Client-side schema utilities for local state management
  • Powerful developer tools for debugging and performance monitoring

To use Apollo Client, first install the core @apollo/client package:

npm install @apollo/client graphql

You‘ll then create an ApolloClient instance and provide that to an ApolloProvider component at the root of your application:

import { ApolloClient, InMemoryCache, ApolloProvider } from ‘@apollo/client‘;

const client = new ApolloClient({
  uri: ‘https://api.example.com/graphql‘,
  cache: new InMemoryCache()
});

function App() {
  return (
    <ApolloProvider client={client}>
      {/* Application components */}
    </ApolloProvider>
  );
}

With this setup, you can execute queries in any child component using the useQuery hook:

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

const GET_ROCKET_INVENTORY = gql`
  query GetRocketInventory {
    rockets {
      id
      name
      description
      flickr_images
    }
  }
`;

function RocketInventory() {
  const { loading, error, data } = useQuery(GET_ROCKET_INVENTORY);

  if (loading) return <Loading />;
  if (error) return <Error error={error} />;

  return (
    <div>
      <h2>Available Rockets</h2>
      <ul>
        {data.rockets.map(rocket => (
          <li key={rocket.id}>
            {rocket.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

Here, the useQuery hook executes the GET_ROCKET_INVENTORY query and returns an object with loading, error, and data properties. Apollo Client automatically caches the result and returns it on subsequent executions with the same query and variables.

Alongside useQuery, Apollo provides a useMutation hook for modifying data:

const RESERVE_ROCKET = gql`
  mutation ReserveRocket($rocketId: ID!) {
    reserveRocket(id: $rocketId) {
      success
      message
      tripId      
    }
  }
`;

function ReserveButton({ rocketId }) {
  const [reserveRocket, { loading, error, data }] = useMutation(RESERVE_ROCKET);

  return (
    <button 
      onClick={() => reserveRocket({ variables: { rocketId } })} 
      disabled={loading}
    >
      {loading ? ‘Reserving…‘ : ‘Reserve Rocket‘}
    </button>
  );
}

Mutations can optionally accept variables and return modified data to update the local Apollo cache.

urql

urql is another popular open-source GraphQL client for React. Compared to Apollo, its aim is to be more modular and lightweight.

Some of the key features and differences of urql include:

  • Modular "addon" packages for caching, subscriptions, authentication, etc.
  • Customizable cache and exchange pipeline
  • Smaller bundle size (~7 kB minified + gzipped)
  • Render-as-you-fetch approach for improved perceived performance

Getting started with urql is similar to Apollo. Install the urql package:

npm install urql graphql

Create a client instance and provider:

import { createClient, Provider } from ‘urql‘;

const client = createClient({
  url: ‘https://api.example.com/graphql‘,
});

ReactDOM.render(
  <Provider value={client}>
    <App />
  </Provider>,
  document.getElementById(‘root‘)
);

And execute queries with the useQuery hook:

import { useQuery } from ‘urql‘;

const ROCKET_INVENTORY_QUERY = `
  query {
    rockets {
      id
      name
      description 
      flickr_images
    }
  }
`;

function RocketInventory() {
  const [result, reexecuteQuery] = useQuery({
    query: ROCKET_INVENTORY_QUERY,
  });

  const { data, fetching, error } = result;

  if (fetching) return <Loading />;
  if (error) return <Error error={error} />;

  return (
    <div>
      <h2>Available Rockets</h2>
      <ul>
        {data.rockets.map(rocket => (
          <li key={rocket.id}>
            {rocket.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

One difference is that urql‘s useQuery returns a tuple with the query result object and a reexecuteQuery function to manually refetch data.

Relay

Relay is Facebook‘s own GraphQL client for React applications. It‘s highly opinionated and optimized around performance and scalability for complex applications.

Some standout features of Relay include:

  • Build-time optimizations with ahead-of-time compilation of queries
  • Aggressive local data caching and optimized network utilization
  • Fine-grained subscriptions to data changes
  • Support for local GraphQL schema extensions
  • Framework integrations (e.g. React Native, Next.js)

Using Relay requires adding the Relay compiler to your build process and precompiling GraphQL queries:

// RocketInventory.js
import { graphql, usePreloadedQuery } from ‘react-relay‘;

const RocketInventoryQuery = graphql`
  query RocketInventoryQuery {
    rockets {
      id
      name
      description
      flickr_images
    }
  }
`;

function RocketInventory({ preloadedQuery }) {
  const data = usePreloadedQuery(RocketInventoryQuery, preloadedQuery);

  return (
    <div>
      <h2>Available Rockets</h2>
      <ul>
        {data.rockets.map(rocket => (
          <li key={rocket.id}>
            {rocket.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

While powerful, Relay has a steeper learning curve and may be overkill for simpler applications compared to Apollo or urql.

Lightweight Alternatives

For applications that don‘t require all the features of the above libraries, lightweight alternatives using React Query or SWR to manage data fetching can be a good choice.

React Query is a library for handling asynchronous state and caching in React applications. While not GraphQL-specific, it works well with minimal GraphQL clients like graphql-request.

import { useQuery } from ‘react-query‘;
import { request, gql } from ‘graphql-request‘;

const endpoint = ‘https://api.example.com/graphql‘;

const ROCKET_INVENTORY_QUERY = gql`
  query RocketInventory {
    rockets {
      id
      name
      description
      flickr_images  
    }
  }
`;

function RocketInventory() {
  const { isLoading, error, data } = useQuery(‘rocketInventory‘, () =>
    request(endpoint, ROCKET_INVENTORY_QUERY)
  );

  if (isLoading) return <Loading />;
  if (error) return <Error error={error} />; 

  return (
    <div>
      <h2>Available Rockets</h2>
      <ul>
        {data.rockets.map(rocket => (
          <li key={rocket.id}>
            {rocket.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

This approach foregoes some of the automatic caching and state management of a full GraphQL client but can be a good choice for applications with simple data requirements.

Testing and Monitoring GraphQL APIs

As with any API, testing and monitoring GraphQL endpoints is crucial to maintaining a high-quality, reliable application. Some key considerations and tooling options include:

  • Schema change validation: Tools like Apollo Studio can detect changes to a GraphQL schema that may break existing client queries
  • Automated testing: End-to-end testing tools like Cypress can be used to execute and validate GraphQL queries
  • Monitoring and analytics: Platforms like Apollo Graph Manager provide monitoring, performance tracing, and usage analytics for GraphQL APIs

By incorporating testing and monitoring into their workflows, teams can confidently iterate on GraphQL APIs and maintain application health as they scale.

GraphQL at Scale

GraphQL is increasingly being adopted by large organizations to power mission-critical applications. Some notable examples include:

  • GitHub used GraphQL to redesign their API and has since made it the primary means of interacting programmatically with their platform
  • Airbnb transitioned large parts of their architecture to GraphQL and saw major improvements in developer productivity and application performance
  • The New York Times uses Apollo Client and Server to power their website and native applications

Some common approaches these organizations employ include:

  • Federated architectures with multiple GraphQL services stitched together into a single schema
  • Using GraphQL as an abstraction layer over legacy services and databases
  • Optimizing client-side caching and state management with libraries like Apollo and Relay

The GraphQL specification and surrounding tooling ecosystem has matured to support these kinds of large-scale deployments.

The Future of GraphQL

Looking ahead, there are a number of exciting developments in the GraphQL community:

As the ecosystem continues to mature and evolve, GraphQL is well-positioned to become the default choice for building modern, data-driven applications with React and beyond.

Conclusion

GraphQL provides a powerful, flexible approach to fetch data for React applications. By enabling clients to query for exactly the data they need, GraphQL can simplify data fetching code, improve performance, and enable rapid development as requirements change.

This guide covered the following major approaches to fetching GraphQL data in React:

  1. Using a fully-featured client like Apollo or Relay
  2. Using a modular client like urql
  3. Combining a lightweight client with React Query

It also explored key considerations and best practices around testing, monitoring, and scaling GraphQL APIs.

Ultimately, the choice of how to integrate GraphQL into a React application depends on factors like performance requirements, team expertise, and anticipated growth. But with its strong typing, client-driven architecture, and vibrant ecosystem, GraphQL is a compelling choice for any modern web application.

References

[^1]: State of JS 2022: https://2022.stateofjs.com/en-US/libraries/graphql-clients

Similar Posts