Apollo Docs
/

Queries

Learn how to fetch data with the useQuery hook


Fetching data in a simple, predictable way is one of the core features of Apollo Client. In this guide, you'll learn how to fetch GraphQL data with the useQuery hook 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 when used with useQuery.

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 useQuery hook

The useQuery hook, which is based on React's Hooks API, is one of the most important building blocks of your Apollo application. To run a query, just pass a GraphQL query string wrapped with the gql function into useQuery as the first parameter, when you call useQuery within a React component. When your component renders, useQuery will return 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 pass it into our useQuery hook. When we get back the loading, error, and data properties that the useQuery hook provides for us, we can intelligently render different UI elements depending on the state of our query. Let's see what this looks like!

import gql from 'graphql-tag';
import { useQuery } from '@apollo/react-hooks';

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

function Dogs({ onDogSelected }) {
  const { loading, error, data } = useQuery(GET_DOGS);

  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>
  );
}

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 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 returned by the useQuery hook. Let's dive deeper into what's happening behind the scenes with Apollo Client when we fetch data with useQuery.

  1. When useQuery is called the first time, Apollo Client creates an observable for our query. Our component subscribes to the result of the query via the Apollo Client cache.
  2. Initially, 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 useQuery hook subscribes to the result internally, 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
    }
  }
`;

function DogPhoto({ breed }) {
  const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

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

You'll notice that we're using a new configuration option with our useQuery hook. The variables option 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 option into the useQuery hook 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 that are returned by the useQuery hook.

function DogPhoto({ breed }) {
  const { loading, error, data, startPolling, stopPolling } = useQuery(
    GET_DOG_PHOTO,
    {
      variables: { breed },
      skip: !breed,
      pollInterval: 500,
    },
  );

  if (loading) return null;
  if (error) return `Error! ${error}`;

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

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.

function DogPhoto({ breed }) {
  const { loading, error, data, refetch } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
    skip: !breed,
  });

  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>
  );
}

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 result object returned by useQuery. These properties are helpful 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 in the useQuery result object. We also need to set the option notifyOnNetworkStatusChange to true so our query component re-renders while a refetch is in flight.

function DogPhoto({ breed }) {
  const { loading, error, data, refetch, networkStatus } = useQuery(
    GET_DOG_PHOTO,
    {
      variables: { breed },
      skip: !breed,
      notifyOnNetworkStatusChange: true,
    },
  );

  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>
  );
}

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 useQuery errorPolicy option. 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 useQuery result object 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 and renders a component that calls the useQuery hook, 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?

One option is to leverage the useApolloClient hook and directly call client.query() instead.

import React from 'react';
import { useApolloClient } from '@apollo/react-hooks';

function DelayedQuery() {
  const [dog, setDog] = useState(null);
  const client = useApolloClient();

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

While fetching this way gives you fine-grained control over how the query is called, it is more verbose. As an alternative, we recommend using the useLazyQuery hook.

The useLazyQuery hook acts just like the useQuery hook, with one main exception. When useLazyQuery is called, it does not immediately execute the defined query. Instead, it returns an execution function in its result tuple, that can then be triggered whenever you're ready to call the query.

import React from 'react';
import { useLazyQuery } from '@apollo/react-hooks';

function DelayedQuery() {
  const [dog, setDog] = useState(null);
  const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);

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

  if (data && data.dog) {
    setDog(data.dog);
  }

  return (
    <div>
      {dog && <img src={dog.displayImage} />}
      <button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
        Click me!
      </button>
    </div>
  );
}

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

useQuery API overview

If you're looking for an overview of all the options useQuery accepts, and its result properties, look no further! Most useQuery calls 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 useQuery hook API in more detail with usage examples, visit our reference guide.

Note: If you're using React Apollo's Query render prop component, the option/result details listed below are still valid (options are component props and results are passed into the render prop function). The only difference is that a query prop (which holds a GraphQL query document parsed into an AST by graphql-tag) is also required.

Options

The useQuery hook accepts the following options.

OptionTypeDescription
queryDocumentNodeA GraphQL query document parsed into an AST by graphql-tag. Optional for the useQuery Hook since the query can be passed in as the first parameter to the Hook. Required for the Query component.
variables{ [key: string]: any }An object containing all of the variables your query needs to execute
pollIntervalnumberSpecifies the interval in ms at which you want your component to poll for data. Defaults to 0 (no polling).
notifyOnNetworkStatusChangebooleanWhether updates to the network status or network error should re-render your component. Defaults to false.
fetchPolicyFetchPolicyHow you want your component to interact with the Apollo cache. Defaults to "cache-first".
errorPolicyErrorPolicyHow you want your component to handle network and GraphQL errors. Defaults to "none", which means we treat GraphQL errors as runtime errors.
ssrbooleanPass in false to skip your query during server-side rendering.
displayNamestringThe name of your component to be displayed in React DevTools. Defaults to 'Query'.
skipbooleanIf skip is true, the query will be skipped entirely. Not available with useLazyQuery.
onCompleted(data: TData | {}) => voidA callback executed once your query successfully completes.
onError(error: ApolloError) => voidA callback executed in the event of an error.
contextRecord<string, any>Shared context between your component and your network interface (Apollo Link). Useful for setting headers from props or sending information to the request function of Apollo Boost.
partialRefetchbooleanIf 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.
clientApolloClientAn ApolloClient instance. By default useQuery / Query uses the client passed down via context, but a different client can be passed in.
returnPartialDatabooleanOpt into receiving partial results from the cache for queries that are not fully satisfied by the cache. false by default.

Result

After being called the useQuery hook will return a result object with the following properties. This object contains your query result, plus some helpful functions for refetching, dynamic polling, and pagination.

PropertyTypeDescription
dataTDataAn object containing the result of your GraphQL query. Defaults to undefined.
loadingbooleanA boolean that indicates whether the request is in flight
errorApolloErrorA runtime error with graphQLErrors and networkError properties
variables{ [key: string]: any }An object containing the variables the query was called with
networkStatusNetworkStatusA 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) => voidThis function sets up an interval in ms and fetches the query each time the specified interval passes.
stopPolling() => voidThis function stops the query from polling.
subscribeToMore(options: { document: DocumentNode, variables?: TVariables, updateQuery?: Function, onError?: Function}) => () => voidA function that sets up a subscription. subscribeToMore returns a function that you can use to unsubscribe.
updateQuery(previousResult: TData, options: { variables: TVariables }) => TDataA function that allows you to update the query's result in the cache outside the context of a fetch, mutation, or subscription
clientApolloClientYour ApolloClient instance. Useful for manually firing queries or writing data to the cache.
calledbooleanA boolean indicating if the query function has been called, used by useLazyQuery (not set for useQuery / Query).

Next steps

Learning how to leverage the useQuery hook 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 using the useMutation hook 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 the useMutation hook.
  • Local state management: Learn how to query local data.
  • Pagination: Building lists has never been easier thanks to Apollo Client's fetchMore function. Learn more in our pagination tutorial.