6. Fetch data with queries

Learn how to fetch data with the useQuery hook

Time to accomplish: 15 Minutes

Apollo Client simplifies fetching data from a graph API because it intelligently caches your data, as well as tracks loading and error state. In the previous section, we learned how to fetch a sample query with Apollo Client without using a view integration. In this section, we'll learn how to use the useQuery hook from @apollo/react-hooks to fetch more complex queries and execute features like pagination.

The useQuery hook

The useQuery hook is one of the most important building blocks of an Apollo app. It's a React Hook that fetches a GraphQL query and exposes the result so you can render your UI based on the data it returns.

The useQuery hook leverages React's Hooks API to fetch and load data from queries into our UI. It exposes error, loading and data properties through a result object, that help us populate and render our component. Let's see an example:

Fetching a list

To create a component with useQuery, import useQuery from @apollo/react-hooks, pass your query wrapped with gql in as the first parameter, then wire your component up to use the loading, data, and error properties on the result object to render UI in your app.

First, we're going to build a GraphQL query that fetches a list of launches. We're also going to import some components that we will need in the next step. Navigate to src/pages/launches.js to get started and copy the code below into the file.

Here, we're defining a query to fetch a list of launches by calling the launches query from our schema. The launches query returns an object type with a list of launches, in addition to the cursor of the paginated list and whether or not the list hasMore launches. We need to wrap the query with the gql function in order to parse it into an AST.

Now, let's pass that query to Apollo's useQuery component to render the list:

To render the list, we pass the GET_LAUNCHES query from the previous step into our useQuery hook. Then, depending on the state of loading, error, and data, we either render a loading indicator, an error message, or a list of launches.

We're not done yet! Right now, this query is only fetching the first 20 launches from the list. To fetch the full list of launches, we need to build a pagination feature that displays a Load More button for loading more items on the screen. Let's learn how!

Build a paginated list

Apollo Client has built-in helpers to make adding pagination to our app much easier than it would be if we were writing the logic ourselves.

To build a paginated list with Apollo, we first need to destructure the fetchMore function from the useQuery result object:

Now that we have fetchMore, let's connect it to a Load More button to fetch more items when it's clicked. To do this, we will need to specify an updateQuery function on the return object from fetchMore that tells the Apollo cache how to update our query with the new items we're fetching.

Copy the code below and add it above the closing </Fragment> tag in the render prop function we added in the previous step.

First, we check to see if we have more launches available in our query. If we do, we render a button with a click handler that calls the fetchMore function from Apollo. The fetchMore function receives new variables for the list of launches query, which is represented by our cursor.

We also define the updateQuery function to tell Apollo how to update the list of launches in the cache. To do this, we take the previous query result and combine it with the new query result from fetchMore.

In the next step, we'll learn how to wire up the launch detail page to display a single launch when an item in the list is clicked.

Fetching a single launch

Let's navigate to src/pages/launch.js to build out our detail page. First, we should import some components and define our GraphQL query to get the launch details.

Now that we have a query, let's render a component with useQuery to execute it. This time, we'll also need to pass in the launchId as a variable to the query, which we'll do by adding a variables option to useQuery. The launchId comes through as a prop from the router.

Just like before, we use the status of the query to render either a loading or error state, or data when the query completes.

Using fragments to share code

You may have noticed that the queries for fetching a list of launches and fetching a launch detail share a lot of the same fields. When we have two GraphQL operations that contain the same fields, we can use a fragment to share fields between the two.

To learn how to build a fragment, navigate to src/pages/launches.js and copy the code below into the file:

We define a GraphQL fragment by giving it a name (LaunchTile) and defining it on a type on our schema (Launch). The name we give our fragment can be anything, but the type must correspond to a type in our schema.

To use our fragment in our query, we import it into the GraphQL document and use the spread operator to spread the fields into our query:

Next, let's render a component with useQuery to fetch a logged in user's list of trips. By default, Apollo Client's fetch policy is cache-first, which means it checks the cache to see if the result is there before making a network request. Since we want this list to always reflect the newest data from our graph API, we set the fetchPolicy for this query to network-only:

If you try to render this query, you'll notice that it returns null. This is because we need to implement our login feature first. We're going to tackle login in the next section.

Now that we've learned how to leverage useQuery to build components that can fetch a paginated list, share fragments, and customize the fetch policy, it's time to progress to the next section so we can learn how to update data with mutations!