Queries

Learn how to fetch data with Query components

Fetching data in a simple, predictable way is one of the core features of Apollo Client. In this guide, you’ll learn how to build Query components in order to fetch GraphQL data and attach the result to your UI. You’ll also learn how Apollo Client simplifies your data management code by tracking error and loading states for you.

This page assumes some familiarity with building GraphQL queries. If you’d like a refresher, we recommend reading this guide and practicing running queries in GraphiQL. Since Apollo Client queries are just standard GraphQL, you can be sure that any query that successfully runs in GraphiQL will also run in an Apollo Query component.

The following examples assume that you’ve already set up Apollo Client and have wrapped your React app in an ApolloProvider component. Read our getting started guide if you need help with either of those steps.

If you’d like to follow along with the examples, open up our starter project on CodeSandbox and our sample GraphQL server on this CodeSandbox. You can view the completed version of the app here.

The Query component

The Query component is one of the most important building blocks of your Apollo application. To create a Query component, just pass a GraphQL query string wrapped with the gql function to this.props.query and provide a function to this.props.children that tells React what to render. The Query component is an example of a React component that uses the render prop pattern. React will call the render prop function you provide with an object from Apollo Client containing loading, error, and data properties that you can use to render your UI. Let’s look at an example:

First, let’s create our GraphQL query. Remember to wrap your query string in the gql function in order to parse it into a query document. Once we have our GraphQL query, let’s attach it to our Query component by passing it to the query prop.

We also need to provide a function as a child to our Query component that will tell React what we want to render. We can use the loading, error, and data properties that the Query component provides for us in order to intelligently render different UI depending on the state of our query. Let’s see what this looks like!

import gql from "graphql-tag";
import { Query } from "react-apollo";

const GET_DOGS = gql`
  {
    dogs {
      id
      breed
    }
  }
`;

const Dogs = ({ onDogSelected }) => (
  <Query query={GET_DOGS}>
    {({ loading, error, data }) => {
      if (loading) return "Loading...";
      if (error) return `Error! ${error.message}`;

      return (
        <select name="dog" onChange={onDogSelected}>
          {data.dogs.map(dog => (
            <option key={dog.id} value={dog.breed}>
              {dog.breed}
            </option>
          ))}
        </select>
      );
    }}
  </Query>
);

If you render Dogs within your App component, you’ll first see a loading state and then a form with a list of dog breeds once Apollo Client receives the data from the server. When the form value changes, we’re going to send the value to a parent component via this.props.onDogSelected, which will eventually pass the value to a DogPhoto component.

In the next step, we’re going to hook our form up to a more complex query with variables by building a DogPhoto component.

Receiving data

You’ve already seen a preview of how to work with the result of your query in the render prop function. Let’s dive deeper into what’s happening behind the scenes with Apollo Client when we fetch data from a Query component.

  1. When the Query component mounts, Apollo Client creates an observable for our query. Our component subscribes to the result of the query via the Apollo Client cache.
  2. First, we try to load the query result from the Apollo cache. If it’s not in there, we send the request to the server.
  3. Once the data comes back, we normalize it and store it in the Apollo cache. Since the Query component subscribes to the result, it updates with the data reactively.

To see Apollo Client’s caching in action, let’s build our DogPhoto component. DogPhoto accepts a prop called breed that reflects the current value of our form from the Dogs component above.

const GET_DOG_PHOTO = gql`
  query Dog($breed: String!) {
    dog(breed: $breed) {
      id
      displayImage
    }
  }
`;

const DogPhoto = ({ breed }) => (
  <Query query={GET_DOG_PHOTO} variables={{ breed }}>
    {({ loading, error, data }) => {
      if (loading) return null;
      if (error) return `Error!: ${error}`;

      return (
        <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
      );
    }}
  </Query>
);

You’ll notice there is a new configuration option on our Query component. The prop variables is an object containing the variables we want to pass to our GraphQL query. In this case, we want to pass the breed from the form into our query.

Try selecting “bulldog” from the list to see its photo show up. Then, switch to another breed and switch back to “bulldog”. You’ll notice that the bulldog photo loads instantaneously the second time around. This is the Apollo cache at work!

Next, let’s learn some techniques for ensuring our data is fresh, such as polling and refetching.

Polling and refetching

It’s awesome that Apollo Client caches your data for you, but what should we do when we want fresh data? Two solutions are polling and refetching.

Polling can help us achieve near real-time data by causing the query to refetch on a specified interval. To implement polling, simply pass a pollInterval prop to the Query component with the interval in ms. If you pass in 0, the query will not poll. You can also implement dynamic polling by using the startPolling and stopPolling functions on the result object passed to the render prop function.

const DogPhoto = ({ breed }) => (
  <Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
    pollInterval={500}
  >
    {({ loading, error, data, startPolling, stopPolling }) => {
      if (loading) return null;
      if (error) return `Error!: ${error}`;

      return (
        <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
      );
    }}
  </Query>
);

By setting the pollInterval to 500, you should see a new dog image every .5 seconds. Polling is an excellent way to achieve near-realtime data without the complexity of setting up GraphQL subscriptions.

What if you want to reload the query in response to a user action instead of an interval? That’s where the refetch function comes in! Here, we’re adding a button to our DogPhoto component that will trigger a refetch when clicked. refetch takes variables, but if we don’t pass in new variables, it will use the same ones from our previous query.

const DogPhoto = ({ breed }) => (
  <Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
  >
    {({ loading, error, data, refetch }) => {
      if (loading) return null;
      if (error) return `Error!: ${error}`;

      return (
        <div>
          <img
            src={data.dog.displayImage}
            style={{ height: 100, width: 100 }}
          />
          <button onClick={() => refetch()}>Refetch!</button>
        </div>
      );
    }}
  </Query>
);

If you click the button, you’ll notice that our UI updates with a new dog photo. Refetching is an excellent way to guarantee fresh data, but it introduces some added complexity with loading state. In the next section, you’ll learn strategies to handle complex loading and error state.

Loading and error state

We’ve already seen how Apollo Client exposes our query’s loading and error state in the render prop function. These properties are helpful for when the query initially loads, but what happens to our loading state when we’re refetching or polling?

Let’s go back to our refetching example from the previous section. If you click on the refetch button, you’ll see that the component doesn’t re-render until the new data arrives. What if we want to indicate to the user that we’re refetching the photo?

Luckily, Apollo Client provides fine-grained information about the status of our query via the networkStatus property on the result object in the render prop function. We also need to set the prop notifyOnNetworkStatusChange to true so our query component re-renders while a refetch is in flight.

const DogPhoto = ({ breed }) => (
  <Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
    notifyOnNetworkStatusChange
  >
    {({ loading, error, data, refetch, networkStatus }) => {
      if (networkStatus === 4) return "Refetching!";
      if (loading) return null;
      if (error) return `Error!: ${error}`;

      return (
        <div>
          <img
            src={data.dog.displayImage}
            style={{ height: 100, width: 100 }}
          />
          <button onClick={() => refetch()}>Refetch!</button>
        </div>
      );
    }}
  </Query>
);

The networkStatus property is an enum with number values from 1-8 representing a different loading state. 4 corresponds to a refetch, but there are also numbers for polling and pagination. For a full list of all the possible loading states, check out the reference guide.

While not as complex as loading state, responding to errors in your component is also customizable via the errorPolicy prop on the Query component. The default value for errorPolicy is “none” in which we treat all GraphQL errors as runtime errors. In the event of an error, Apollo Client will discard any data that came back with the request and set the error property in the render prop function to true. If you’d like to show any partial data along with any error information, set the errorPolicy to “all”.

Manually firing a query

When React mounts a Query component, Apollo Client automatically fires off your query. What if you wanted to delay firing your query until the user performs an action, such as clicking on a button? For this scenario, we want to use an ApolloConsumer component and directly call client.query() instead.

import React, { Component } from 'react';
import { ApolloConsumer } from 'react-apollo';

class DelayedQuery extends Component {
  state = { dog: null };

  onDogFetched = dog => this.setState(() => ({ dog }));

  render() {
    return (
      <ApolloConsumer>
        {client => (
          <div>
            {this.state.dog && <img src={this.state.dog.displayImage} />}
            <button
              onClick={async () => {
                const { data } = await client.query({
                  query: GET_DOG_PHOTO,
                  variables: { breed: "bulldog" }
                });
                this.onDogFetched(data.dog);
              }}
            >
              Click me!
            </button>
          </div>
        )}
      </ApolloConsumer>
    );
  }
}

Fetching this way is quite verbose, so we recommend trying to use a Query component if at all possible!

If you’d like to view a complete version of the app we just built, you can check out the CodeSandbox here.

Query API overview

If you’re looking for an overview of all the props Query accepts and its render prop function, look no further! Most Query components will not need all of these configuration options, but it’s useful to know that they exist. If you’d like to learn about the Query component API in more detail with usage examples, visit our reference guide.

Props

The Query component accepts the following props. Only query and children are required.

query: DocumentNode
A GraphQL query document parsed into an AST by graphql-tag. Required
children: (result: QueryResult) => React.ReactNode
A function returning the UI you want to render based on your query result. Required
variables: { [key: string]: any }
An object containing all of the variables your query needs to execute
pollInterval: number
Specifies the interval in ms at which you want your component to poll for data. Defaults to 0 (no polling).
notifyOnNetworkStatusChange: boolean
Whether updates to the network status or network error should re-render your component. Defaults to false.
fetchPolicy: FetchPolicy
How you want your component to interact with the Apollo cache. Defaults to “cache-first”.
errorPolicy: ErrorPolicy
How you want your component to handle network and GraphQL errors. Defaults to “none”, which means we treat GraphQL errors as runtime errors.
ssr: boolean
Pass in false to skip your query during server-side rendering.
displayName: string
The name of your component to be displayed in React DevTools. Defaults to ‘Query’.
skip: boolean
If skip is true, the query will be skipped entirely.
onCompleted: (data: TData | {}) => void
A callback executed once your query successfully completes.
onError: (error: ApolloError) => void
A callback executed in the event of an error.
context: Record<string, any>
Shared context between your Query component and your network interface (Apollo Link). Useful for setting headers from props or sending information to the request function of Apollo Boost.
partialRefetch: boolean
If true, perform a query refetch if the query result is marked as being partial, and the returned data is reset to an empty Object by the Apollo Client QueryManager (due to a cache miss). The default value is false for backwards-compatibility’s sake, but should be changed to true for most use-cases.

Render prop function

The render prop function that you pass to the children prop of Query is called with an object (QueryResult) that has the following properties. This object contains your query result, plus some helpful functions for refetching, dynamic polling, and pagination.

data: TData
An object containing the result of your GraphQL query. Defaults to an empty object.
loading: boolean
A boolean that indicates whether the request is in flight
error: ApolloError
A runtime error with graphQLErrors and networkError properties
variables: { [key: string]: any }
An object containing the variables the query was called with
networkStatus: NetworkStatus
A number from 1-8 corresponding to the detailed state of your network request. Includes information about refetching and polling status. Used in conjunction with the notifyOnNetworkStatusChange prop.
refetch: (variables?: TVariables) => Promise<ApolloQueryResult>
A function that allows you to refetch the query and optionally pass in new variables
fetchMore: ({ query?: DocumentNode, variables?: TVariables, updateQuery: Function}) => Promise<ApolloQueryResult>
A function that enables pagination for your query
startPolling: (interval: number) => void
This function sets up an interval in ms and fetches the query each time the specified interval passes.
stopPolling: () => void
This function stops the query from polling.
subscribeToMore: (options: { document: DocumentNode, variables?: TVariables, updateQuery?: Function, onError?: Function}) => () => void
A function that sets up a subscription. subscribeToMore returns a function that you can use to unsubscribe.
updateQuery: (previousResult: TData, options: { variables: TVariables }) => TData
A function that allows you to update the query’s result in the cache outside the context of a fetch, mutation, or subscription
client: ApolloClient
Your ApolloClient instance. Useful for manually firing queries or writing data to the cache.

Next steps

Learning how to build Query components to fetch data is one of the most important skills to mastering development with Apollo Client. Now that you’re a pro at fetching data, why not try building Mutation components to update your data? Here are some resources we think will help you level up your skills:

  • Mutations: Learn how to update data with mutations and when you’ll need to update the Apollo cache. For a full list of options, check out the API reference for Mutation components.
  • Local state management: Learn how to query local data with apollo-link-state.
  • Pagination: Building lists has never been easier thanks to Apollo Client’s fetchMore function. Learn more in our pagination tutorial.
  • Query component video by Sara Vieira: If you need a refresher or learn best by watching videos, check out this tutorial on Query components by Sara!
Edit on GitHub
// search box