DEV Community

loading...
Cover image for 5 Ways to Fetch Data in React from a GraphQL API

5 Ways to Fetch Data in React from a GraphQL API

reedbarger profile image Reed Barger Originally published at reactbootcamp.com ・9 min read

Let's go through the five best ways that you can fetch data with React from a GraphQL API.

While there are a couple of popular libraries which are made to interact with GraphQL APIs from a React application, there are many different ways to fetch data with GraphQL.

I've included code samples that show you how to fetch or "query" data in the shortest code possible and how to get up and running with each of these different methods of connecting React with GraphQL.

Want to build amazing apps with React and GraphQL from front to back? Check out the React Bootcamp.

Getting Started

In these examples, we'll be using the SpaceX GraphQL API to fetch and display the past 10 missions that SpaceX has made.

Feel free to use the code below if you are attempting to connect your React app with a GraphQL API. In our examples, we're going to go from the most advanced GraphQL client library for React to the simplest approach to querying a GraphQL endpoint.

1. Apollo Client

The most popular and comprehensive GraphQL library is Apollo Client.

Not only can you use it to fetch remote data with GraphQL, which we're doing here, but it allows us to manage data locally, both through an internal cache as well as an entire state management API.

To get started with Apollo Client, we need to install both the main Apollo Client dependency, as well as GraphQL:

npm install @apollo/client graphql
Enter fullscreen mode Exit fullscreen mode

The idea behind the Apollo Client is that it will be used across our entire application. To do so, we use a special Apollo Provider component to pass down a created Apollo client down our entire component tree.

When we create our Apollo Client we need to specify a uri value, namely a GraphQL endpoint. Additionally, we need to specify a cache. Apollo Client comes with its own in memory cache, which is used to cache or locally store and manage our queries and their related data:

import React from "react";
import ReactDOM from "react-dom";
import { ApolloProvider, ApolloClient, InMemoryCache } from "@apollo/client";

import App from "./App";

const client = new ApolloClient({
  uri: "https://api.spacex.land/graphql/",
  cache: new InMemoryCache()
});

const rootElement = document.getElementById("root");
ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  rootElement
);
Enter fullscreen mode Exit fullscreen mode

Once we've set up the Provider and client within our App component, we can use all of the different React hooks that Apollo Client gives us for all the different GraphQL operations, which include queries, mutations, and subscriptions. We can even use the created Apollo Client directly using a custom hook called useApolloClient.

Since we're just querying data here, we will use the useQuery hook.

We will include a GraphQL query as its first argument to write our query. We use the function gql, which does a number of things, such as giving us editor syntax highlighting and the auto formatting functionality if we use the tool Prettier for our project.

Once we execute this query, we get back the values data, loading, and error:

import React from "react";
import { useQuery, gql } from "@apollo/client";

const FILMS_QUERY = gql`
  {
    launchesPast(limit: 10) {
      id
      mission_name
    }
  }
`;

export default function App() {
  const { data, loading, error } = useQuery(FILMS_QUERY);

  if (loading) return "Loading...";
  if (error) return <pre>{error.message}</pre>

  return (
    <div>
      <h1>SpaceX Launches</h1>
      <ul>
        {data.launchesPast.map((launch) => (
          <li key={launch.id}>{launch.mission_name}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Before we display our data, our missions, we want to handle the loading state. When we are in a loading state, we are fetching the query from a remote endpoint.

We also want to handle the case that there's an error. We can simulate an error by making a syntax error in our query, such as querying for a field that doesn't exist. To handle that error, we can conveniently return and display a message from error.message.

2. Urql

Another fully featured library that connects react apps with GraphQL APIs is urql.

It attempts to give us many of the features and syntax Apollo has while being a little bit smaller in size and requiring less setup code. It gives us caching capabilities if we choose, but it does not include an integrated state management library like Apollo does.

To use urql as your GraphQL client library, you'll need to install the packages urql and GraphQL.

npm install urql graphql
Enter fullscreen mode Exit fullscreen mode

Just like Apollo, we want to use the dedicated Provider component, and create a client with our GraphQL endpoint. Note that we do not need to specify a cache out of the box.

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { createClient, Provider } from 'urql';

const client = createClient({
  url: 'https://api.spacex.land/graphql/',
});

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider value={client}>
    <App />
  </Provider>,
  rootElement
);
Enter fullscreen mode Exit fullscreen mode

Very similar to Apollo, urql gives us custom hooks that handle all the standard GraphQL operations, and therefore have similar names.

Again, we can use the useQuery hook from the urql package. Although instead of needing the function gql, we can drop it and just use a template literal to write our query.

When calling useQuery, we get back an array which we can destructure as an array instead of as an object. The first element in this array is an object, called result, which gives us a number of properties that we can destructure: data, fetching and error.

import React from "react";
import { useQuery } from 'urql';

const FILMS_QUERY = `
  {
    launchesPast(limit: 10) {
      id
      mission_name
    }
  }
`;

export default function App() {
  const [result] = useQuery({
    query: FILMS_QUERY,
  });

  const { data, fetching, error } = result;

  if (fetching) return "Loading...";
  if (error) return <pre>{error.message}</pre>

  return (
    <div>
      <h1>SpaceX Launches</h1>
      <ul>
        {data.launchesPast.map((launch) => (
          <li key={launch.id}>{launch.mission_name}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In an identical fashion to displaying the data that we fetch with Apollo, we can handle both our error and loading states while we're fetching our remote data.

3. React Query + GraphQL Request

It's important to note at this point that you don't need a sophisticated, heavyweight GraphQL client library like urql or Apollo to interact with your GraphQL API, as we will see later.

Libraries like Apollo and urql were created not only to help us perform all the standard GraphQL operations, but to better manage the server state in our React client through a number of additional tools. Along with the fact that they come with custom hooks that make managing repetitive tasks like handling loading, error, and other related states simple.

With that in mind, let's take a look at how we can use a very pared-down GraphQL library for our data fetching and combine that with a better means of managing and caching that server state that we're bringing into our application. The way that we can fetch data very simply is with the help of the package graphql-request.

GraphQL Request is a library that doesn't require us to set up a client or a Provider component. It is essentially a function that just accepts an endpoint and a query. Very similar to an HTTP client, we just have to pass in those two values and we get back our data.

Now if we want to manage that state across our app, we can use a great library normally used for interacting with Rest APIs, but is equally helpful for GraphQL APIs, and that is React Query. It gives us some very similarly named React Hooks, useQuery and useMutation that perform identical tasks to what the Apollo and urql hooks perform.

React Query also gives us a bunch of tools for managing our state, along with an integrated Dev Tools component that allows us to see what's being stored in React Query's built-in cache.

By pairing our very basic GraphQL client, GraphQL request, with React Query we get all the power of a library like urql or Apollo.

To get started with this pairing, we just need to install React Query and GraphQL Request:

npm install react-query graphql-request
Enter fullscreen mode Exit fullscreen mode

We use React Query's Provider component and create a query client where we can set some default data fetching settings if we like, and then within our app component itself, or any child components of App we can use the useQuery hook.

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { QueryClient, QueryClientProvider } from "react-query";

const client = new QueryClient();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={client}>
    <App />
  </QueryClientProvider>,
  rootElement
);
Enter fullscreen mode Exit fullscreen mode

To store the result of our operation in the React Query cache, we just need to give it a key value as the first argument to serve as an identifier. This allows us to very easily reference and pull data from the cache, as well as to refetch or invalidate a given query to fetch updated data.

Since we're fetching launch data will call this query "launches".

Once again, this hook will return the result of making that request. For the second argument to useQuery, we need to specify how to fetch that data and React Query will take care of resolving the promise that GraphQL request returns.

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

const endpoint = "https://api.spacex.land/graphql/";
const FILMS_QUERY = gql`
  {
    launchesPast(limit: 10) {
      id
      mission_name
    }
  }
`;

export default function App() {
  const { data, isLoading, error } = useQuery("launches", () => {
    return request(endpoint, FILMS_QUERY);
  });

  if (isLoading) return "Loading...";
  if (error) return <pre>{error.message}</pre>;

  return (
    <div>
      <h1>SpaceX Launches</h1>
      <ul>
        {data.launchesPast.map((launch) => (
          <li key={launch.id}>{launch.mission_name}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Similar to Apollo, we get back an object that we can destructure to get the values for the data, as well as whether or not we're in the loading state, and the error state.

4. React Query + Axios

We can use even simpler HTTP client libraries that have no relationship to GraphQL to fetch our data.

In this case, we can use the popular library axios. Once again we'll pair it with React Query to get all the special hooks and state management.

npm install react-query axios
Enter fullscreen mode Exit fullscreen mode

Using an HTTP client like Axios to make perform a query from a GraphQL API requires performing a POST request to our API endpoint. For the data that we send along in the request, we will provide an object with a property called query, which will be set to our films query.

With axios, we're going to need to include a little bit more information about how to resolve this promise and get back our data. We need to tell React Query where the data is so it can be put on the data property that useQuery returns.

In particular, we get the data back on the data property of response.data:

import React from "react";
import axios from "axios";
import { useQuery } from "react-query";

const endpoint = "https://api.spacex.land/graphql/";
const FILMS_QUERY = `
  {
    launchesPast(limit: 10) {
      id
      mission_name
    }
  }
`;

export default function App() {
  const { data, isLoading, error } = useQuery("launches", () => {
    return axios({
      url: endpoint,
      method: "POST",
      data: {
        query: FILMS_QUERY
      }
    }).then(response => response.data.data);
  });

  if (isLoading) return "Loading...";
  if (error) return <pre>{error.message}</pre>;

  return (
    <div>
      <h1>SpaceX Launches</h1>
      <ul>
        {data.launchesPast.map((launch) => (
          <li key={launch.id}>{launch.mission_name}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

5. React Query + Fetch API

The easiest way of all these different approaches to fetch data is to just use React query plus the fetch API.

Since the fetch API is included in all modern browsers, we do not need to install a third-party library, we only need to install react-query within our application.

npm install react-query
Enter fullscreen mode Exit fullscreen mode

Once we have the React Query client provided to the entire app, we can just swap out our axios code that we have with fetch.

What's a little bit different is that we need to specify a header that includes the content type of the data that we want back from our request. In this case, it is JSON data.

We also need to stringify the object that we're sending as our payload with a query property that is set to our films query:

import React from "react";
import axios from "axios";
import { useQuery } from "react-query";

const endpoint = "https://api.spacex.land/graphql/";
const FILMS_QUERY = `
  {
    launchesPast(limit: 10) {
      id
      mission_name
    }
  }
`;

export default function App() {
  const { data, isLoading, error } = useQuery("launches", () => {
    return fetch(endpoint, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query: FILMS_QUERY })
    })
      .then((response) => {
        if (response.status >= 400) {
          throw new Error("Error fetching data");
        } else {
          return response.json();
        }
      })
      .then((data) => data.data);
  });

  if (isLoading) return "Loading...";
  if (error) return <pre>{error.message}</pre>;

  return (
    <div>
      <h1>SpaceX Launches</h1>
      <ul>
        {data.launchesPast.map((launch) => (
          <li key={launch.id}>{launch.mission_name}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

One benefit of using axios over fetch is that it automatically handles errors for us. With fetch, as you can see in the code below, we need to check for a certain status code, in particular a status code above 400.

This means that our request resolves to an error. If that's the case, we need to manually throw an error, which will be caught by our useQuery hook. Otherwise, if it's a 200 or 300 range response, meaning the request was successful, we simply return the JSON data and display it.

Conclusion

This article was dedicated to showing you a number of different approaches to effectively fetching data from a GraphQL API with React.

From these options, hopefully you can evaluate which is most appropriate for you and your applications, as well as some helpful code that will enable you to start using these tools and libraries much faster.

Enjoy this post? Join The React Bootcamp

The React Bootcamp takes everything you should know about learning React and bundles it into one comprehensive package, including videos, cheatsheets, plus special bonuses.

Gain the insider information hundreds of developers have already used to master React, find their dream jobs, and take control of their future:

The React Bootcamp

Click here to be notified when it opens

Discussion (3)

pic
Editor guide
Collapse
ajcwebdev profile image
anthony-campolo • Edited

Awesome article Reed, I really like how you structured the code samples. I think you did a good job of keeping the project structure and queries as similar as possible so they're consistent across examples.

This makes it easier to clearly see how each specific library API differs from the others and also makes it easier to know exactly what code would need to be modified if you wanted to migrate from one to another.

Since you've included React Query in a lot of these examples I'm curious if you have tried out swr, rtk-query or any other newer data fetching libraries?

Collapse
_zoka profile image
Mahmoud Ibrahiam

Thank you, it's a great and useful article, I'm a big fan of your articles on freecodecamp.

Collapse
reedbarger profile image
Reed Barger Author

Thank you!