Queries
Fetch data with the useQuery hook
This article shows how to fetch GraphQL data in React with the useQuery
hook and attach the result to your UI. You'll also learn how Apollo Client simplifies data management code by tracking error and loading states for you.
Prerequisites
This article assumes you're familiar with building basic GraphQL queries. If you need a refresher, we recommend this guide. You can also build example queries against Apollo's full-stack tutorial server.
This article also assumes that you've already set up Apollo Client and have wrapped your React app in an ApolloProvider
component. For more information, see the getting started guide.
To follow along with the examples below, open up our starter project and sample GraphQL server on CodeSandbox. You can view the completed version of the app here.
Executing a query
The useQuery
React hook is the primary API for executing queries in an Apollo application. To run a query within a React component, call useQuery
and pass it a GraphQL query string. When your component renders, useQuery
returns an object from Apollo Client that contains loading
, error
, and data
properties you can use to render your UI.
Note: in Apollo Client >= 3.8, Suspense data fetching hooks are available for querying data within
<Suspense />
boundaries using React 18's new concurrent rendering model. For more information see Apollo Client's Suspense docs.
Let's look at an example. First, we'll create a GraphQL query named GET_DOGS
. Remember to wrap query strings in the gql
function to parse them into query documents:
1import { gql, useQuery } from '@apollo/client';
2
3const GET_DOGS = gql`
4 query GetDogs {
5 dogs {
6 id
7 breed
8 }
9 }
10`;
Next, we'll create a component named Dogs
. Inside it, we'll pass our GET_DOGS
query to the useQuery
hook:
1function Dogs({ onDogSelected }) {
2 const { loading, error, data } = useQuery(GET_DOGS);
3
4 if (loading) return 'Loading...';
5 if (error) return `Error! ${error.message}`;
6
7 return (
8 <select name='dog' onChange={onDogSelected}>
9 {data.dogs.map((dog) => (
10 <option key={dog.id} value={dog.breed}>
11 {dog.breed}
12 </option>
13 ))}
14 </select>
15 );
16}
As our query executes and the values of loading
, error
, and data
change, the Dogs
component can intelligently render different UI elements according to the query's state:
As long as
loading
istrue
(indicating the query is still in flight), the component presents aLoading...
notice.When loading is
false
and there is noerror
, the query has completed. The component renders a dropdown menu that's populated with the list of dog breeds returned by the server.
When the user selects a dog breed from the populated dropdown, the selection is sent to the parent component via the provided onDogSelected
function.
In the next step, we'll associate the dropdown with a more sophisticated query that uses GraphQL variables.
Caching query results
Whenever Apollo Client fetches query results from your server, it automatically caches those results locally. This makes later executions of that same query extremely fast.
To see this caching in action, let's build a new component called DogPhoto
. DogPhoto
accepts a prop called breed
that reflects the current value of the dropdown menu in our Dogs
component:
1const GET_DOG_PHOTO = gql`
2 query Dog($breed: String!) {
3 dog(breed: $breed) {
4 id
5 displayImage
6 }
7 }
8`;
9
10function DogPhoto({ breed }) {
11 const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
12 variables: { breed },
13 });
14
15 if (loading) return null;
16 if (error) return `Error! ${error}`;
17
18 return (
19 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
20 );
21}
Notice that we're providing a configuration option (variables
) to the useQuery
hook this time. The variables
option is an object that contains all of the variables we want to pass to our GraphQL query. In this case, we want to pass the currently selected breed
from the dropdown.
Select bulldog
from the dropdown to see its photo appear. Then switch to another breed, and then switch back to bulldog
. You'll notice that the bulldog photo loads instantly the second time around. This is the cache at work!
Next, let's learn some techniques for ensuring that our cached data is fresh.
Updating cached query results
Sometimes, you want to make sure that your query's cached data is up to date with your server's data. Apollo Client supports two strategies for this: polling and refetching.
Polling
Polling provides near-real-time synchronization with your server by executing your query periodically at a specified interval. To enable polling for a query, pass a pollInterval
configuration option to the useQuery
hook with an interval in milliseconds:
1function DogPhoto({ breed }) {
2 const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
3 variables: { breed },
4 pollInterval: 500,
5 });
6
7 if (loading) return null;
8 if (error) return `Error! ${error}`;
9
10 return (
11 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
12 );
13}
By setting pollInterval
to 500, we fetch the current breed's image from the server every 0.5 seconds. Note that if you set pollInterval
to 0
, the query does not poll.
You can also start and stop polling dynamically with the
startPolling
andstopPolling
functions that are returned by theuseQuery
hook. When using these functions, set thepollInterval
configuration option as a parameter of thestartPolling
function.
Refetching
Refetching enables you to refresh query results in response to a particular user action, as opposed to using a fixed interval.
Let's add a button to our DogPhoto
component that calls our query's
refetch
function whenever it's clicked.
You can optionally provide a new variables
object to the refetch
function.
If you avoid passing a variables
object and use only refetch()
, the query
uses the same variables
that it used in its previous execution.
1function DogPhoto({ breed }) {
2 const { loading, error, data, refetch } = useQuery(GET_DOG_PHOTO, {
3 variables: { breed },
4 });
5
6 if (loading) return null;
7 if (error) return `Error! ${error}`;
8
9 return (
10 <div>
11 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
12 <button onClick={() => refetch()}>
13 Refetch new breed!
14 </button>
15 </div>
16 );
17}
Click the button and notice that the UI updates with a new dog photo. Refetching is an excellent way to guarantee fresh data, but it introduces some complexity with loading state. In the next section, we'll cover strategies for handling complex loading and error state.
Providing new variables to refetch
You call refetch
with a new set of variables like so:
1<button
2 onClick={() =>
3 refetch({
4 breed: 'dalmatian', // Always refetches a dalmatian instead of original breed
5 })
6 }
7>
8 Refetch!
9</button>
If you provide new values for some of your original query's variables but not all of them, refetch
uses each omitted variable's original value.
Inspecting loading states
We've already seen that the useQuery
hook exposes our query's current loading state. This is helpful when a query first loads, but what happens to our loading state when we're refetching or polling?
Let's return to our refetching example from the previous section. If you click 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?
The useQuery
hook's result object provides fine-grained information about the status of the query via the networkStatus
property. To take advantage
of this information, we set the notifyOnNetworkStatusChange
option to true
so our query component re-renders while a refetch is in flight:
1import { NetworkStatus } from '@apollo/client';
2
3function DogPhoto({ breed }) {
4 const { loading, error, data, refetch, networkStatus } = useQuery(
5 GET_DOG_PHOTO,
6 {
7 variables: { breed },
8 notifyOnNetworkStatusChange: true,
9 }
10 );
11
12 if (networkStatus === NetworkStatus.refetch) return 'Refetching!';
13 if (loading) return null;
14 if (error) return `Error! ${error}`;
15
16 return (
17 <div>
18 <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
19 <button onClick={() => refetch()}>
20 Refetch!
21 </button>
22 </div>
23 );
24}
Enabling this option also ensures that the value of loading
updates accordingly, even if you don't want to use the more fine-grained information provided by the networkStatus
property.
The networkStatus
property is a NetworkStatus
enum that represents different loading states. Refetch is represented by NetworkStatus.refetch
, and there are also values for polling and pagination. For a full list of all the possible loading states, check out the source.
To view a complete version of the app we just built, check out the CodeSandbox here.
Inspecting error states
You can customize your query error handling by providing the errorPolicy
configuration option to the useQuery
hook. The default value is none
, which tells Apollo Client to treat all GraphQL errors as runtime errors. In this case, Apollo Client discards any query response data returned by the server and sets the error
property in the useQuery
result object.
If you set errorPolicy
to all
, useQuery
does not discard query response data, allowing you to render partial results.
For more information, see Handling operation errors.
Manual execution with useLazyQuery
When React renders a component that calls useQuery
, Apollo Client automatically executes the corresponding query. But what if you want to execute a query in response to a different event, such as a user clicking a button?
The useLazyQuery
hook is perfect for executing queries in response to events besides component rendering. Unlike with useQuery
, when you call useLazyQuery
, it does not immediately execute its associated query. Instead, it returns a query function in its result tuple that you call whenever you're ready to execute the query.
Here's an example:
1import React from 'react';
2import { useLazyQuery } from '@apollo/client';
3
4function DelayedQuery() {
5 const [getDog, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);
6
7 if (loading) return <p>Loading ...</p>;
8 if (error) return `Error! ${error}`;
9
10 return (
11 <div>
12 {data?.dog && <img src={data.dog.displayImage} />}
13 <button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
14 Click me!
15 </button>
16 </div>
17 );
18}
The first item in useLazyQuery
's return tuple is the query function, and the second item is the same result object returned by useQuery
.
As shown above, you can pass options to the query function just like you pass them to useLazyQuery
itself. If you pass a particular option to both, the value you pass to the query function takes precedence. This is a handy way to pass default options to useLazyQuery
and then customize those options in the query function.
variables
passed as options to the hook and merging them with the variables
passed to the query function. If you do not pass variables
to the query function, only the variables
passed to the hook are used in the query execution.For a full list of supported options, see the API reference.
Setting a fetch policy
By default, the useQuery
hook checks the Apollo Client cache to see if all the data you requested is already available locally. If all data is available locally, useQuery
returns that data and doesn't query your GraphQL server. This cache-first
policy is Apollo Client's default fetch policy.
You can specify a different fetch policy for a given query. To do so, include the fetchPolicy
option in your call to useQuery
:
1const { loading, error, data } = useQuery(GET_DOGS, {
2 fetchPolicy: 'network-only', // Doesn't check cache before making a network request
3});
nextFetchPolicy
Since 3.1
You can also specify a query's nextFetchPolicy
. If you do, fetchPolicy
is used for the query's first execution, and nextFetchPolicy
is used to determine how the query responds to future cache updates:
1const { loading, error, data } = useQuery(GET_DOGS, {
2 fetchPolicy: 'network-only', // Used for first execution
3 nextFetchPolicy: 'cache-first', // Used for subsequent executions
4});
For example, this is helpful if you want a query to always make an initial network request, but you're comfortable reading from the cache after that.
nextFetchPolicy
functions
If you want to apply a single nextFetchPolicy
by default, because you find yourself manually providing nextFetchPolicy
for most of your queries, you can configure defaultOptions.watchQuery.nextFetchPolicy
when creating your ApolloClient
instance:
1new ApolloClient({
2 link,
3 client,
4 defaultOptions: {
5 watchQuery: {
6 nextFetchPolicy: 'cache-only',
7 },
8 },
9});
This configuration applies to all client.watchQuery
calls and useQuery
calls that do not otherwise configure nextFetchPolicy
.
If you want more control over how nextFetchPolicy
behaves, you can provide a function instead of a WatchQueryFetchPolicy
string:
1new ApolloClient({
2 link,
3 client,
4 defaultOptions: {
5 watchQuery: {
6 nextFetchPolicy(currentFetchPolicy) {
7 if (
8 currentFetchPolicy === 'network-only' ||
9 currentFetchPolicy === 'cache-and-network'
10 ) {
11 // Demote the network policies (except "no-cache") to "cache-first"
12 // after the first request.
13 return 'cache-first';
14 }
15 // Leave all other fetch policies unchanged.
16 return currentFetchPolicy;
17 },
18 },
19 },
20});
This nextFetchPolicy
function will be called after each request, and uses the currentFetchPolicy
parameter to decide how to modify the fetch policy.
In addition to being called after each request, your nextFetchPolicy
function will also be called when variables change, which by default resets the fetchPolicy
to its initial value, which is often important to trigger a fresh network request for queries that started out with cache-and-network
or network-only
fetch policies.
To intercept and handle the variables-changed
case yourself, you can use the NextFetchPolicyContext
object passed as the second argument to your nextFetchPolicy
function:
1new ApolloClient({
2 link,
3 client,
4 defaultOptions: {
5 watchQuery: {
6 nextFetchPolicy(
7 currentFetchPolicy,
8 {
9 // Either "after-fetch" or "variables-changed", indicating why the
10 // nextFetchPolicy function was invoked.
11 reason,
12 // The rest of the options (currentFetchPolicy === options.fetchPolicy).
13 options,
14 // The original value of options.fetchPolicy, before nextFetchPolicy was
15 // applied for the first time.
16 initialFetchPolicy,
17 // The ObservableQuery associated with this client.watchQuery call.
18 observable,
19 }
20 ) {
21 // When variables change, the default behavior is to reset
22 // options.fetchPolicy to context.initialFetchPolicy. If you omit this logic,
23 // your nextFetchPolicy function can override this default behavior to
24 // prevent options.fetchPolicy from changing in this case.
25 if (reason === 'variables-changed') {
26 return initialFetchPolicy;
27 }
28
29 if (
30 currentFetchPolicy === 'network-only' ||
31 currentFetchPolicy === 'cache-and-network'
32 ) {
33 // Demote the network policies (except "no-cache") to "cache-first"
34 // after the first request.
35 return 'cache-first';
36 }
37
38 // Leave all other fetch policies unchanged.
39 return currentFetchPolicy;
40 },
41 },
42 },
43});
In order to debug these nextFetchPolicy
transitions, it can be useful to add console.log
or debugger
statements to the function body, to see when and why the function is called.
Supported fetch policies
Name | Description |
---|---|
| Apollo Client first executes the query against the cache. If all requested data is present in the cache, that data is returned. Otherwise, Apollo Client executes the query against your GraphQL server and returns that data after caching it.Prioritizes minimizing the number of network requests sent by your application.This is the default fetch policy. |
| Apollo Client executes the query only against the cache. It never queries your server in this case.A cache-only query throws an error if the cache does not contain data for all requested fields. |
| Apollo Client executes the full query against both the cache and your GraphQL server. The query automatically updates if the result of the server-side query modifies cached fields.Provides a fast response while also helping to keep cached data consistent with server data. |
| Apollo Client executes the full query against your GraphQL server, without first checking the cache. The query's result is stored in the cache.Prioritizes consistency with server data, but can't provide a near-instantaneous response when cached data is available. |
| Similar to network-only , except the query's result is not stored in the cache. |
| Uses the same logic as cache-first , except this query does not automatically update when underlying field values change. You can still manually update this query with refetch and updateQueries . |
useQuery
API
Supported options and result fields for the useQuery
hook are listed below.
Most calls to useQuery
can omit the majority of these options, but it's useful to know they exist. To learn about the useQuery
hook API in more detail with usage examples, see the API reference.
Options
The useQuery
hook accepts the following options:
ApolloClient<any>
The instance of ApolloClient
to use to execute the query.
By default, the instance that's passed down via context is used, but you can provide a different instance here.
ErrorPolicy
Specifies how the query handles a response that returns both GraphQL errors and partial results.
For details, see GraphQL error policies.
The default value is none
, meaning that the query result includes error details but not partial results.
(data: MaybeMasked<TData>) => void
A callback function that's called when your query successfully completes with zero errors (or if errorPolicy
is ignore
and partial data is returned).
This function is passed the query's result data
.
(error: ApolloError) => void
A callback function that's called when the query encounters one or more errors (unless errorPolicy
is ignore
).
This function is passed an ApolloError
object that contains either a networkError
object or a graphQLErrors
array, depending on the error(s) that occurred.
boolean
If true, the query is not executed.
The default value is false
.
TVariables
An object containing all of the GraphQL variables your query requires to execute.
Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value.
DefaultContext
If you're using Apollo Link, this object is the initial value of the context
object that's passed along your link chain.
If true
, the in-progress query's associated component re-renders whenever the network status changes or a network error occurs.
The default value is false
.
number
Specifies the interval (in milliseconds) at which the query polls for updated results.
The default value is 0
(no polling).
() => boolean
A callback function that's called whenever a refetch attempt occurs while polling. If the function returns true
, the refetch is skipped and not reattempted until the next poll interval.
boolean
Pass false
to skip executing the query during server-side rendering.
WatchQueryFetchPolicy
Specifies how the query interacts with the Apollo Client cache during execution (for example, whether it checks the cache for results before sending a request to the server).
For details, see Setting a fetch policy.
The default value is cache-first
.
WatchQueryFetchPolicy
Defaults to the initial value of options.fetchPolicy, but can be explicitly configured to specify the WatchQueryFetchPolicy to revert back to whenever variables change (unless nextFetchPolicy intervenes).
WatchQueryFetchPolicy | ((this: WatchQueryOptions<TVariables, TData>, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext<TData, TVariables>) => WatchQueryFetchPolicy)
Specifies the FetchPolicy
to be used after this query has completed.
RefetchWritePolicy
Specifies whether a NetworkStatus.refetch
operation should merge incoming field data with existing data, or overwrite the existing data. Overwriting is probably preferable, but merging is currently the default behavior, for backwards compatibility with Apollo Client 3.x.
boolean
If true
, the query can return partial results from the cache if the cache doesn't contain results for all queried fields.
The default value is false
.
boolean
⚠️ Deprecated
Using
canonizeResults
can result in memory leaks so we generally do not recommend using this option anymore. A future version of Apollo Client will contain a similar feature without the risk of memory leaks.
Whether to canonize cache results before returning them. Canonization takes some extra time, but it speeds up future deep equality comparisons. Defaults to false.
boolean
⚠️ Deprecated
Setting this option is unnecessary in Apollo Client 3, thanks to a more consistent application of fetch policies. It might be removed in a future release.
If true
, causes a query refetch if the query result is detected as partial.
The default value is false
.
Result
After being called, the useQuery
hook returns a result object with the following properties. This object contains your query result, plus some helpful functions for refetching, dynamic polling, and pagination.
MaybeMasked<TData> | undefined
An object containing the result of your GraphQL query after it completes.
This value might be undefined
if a query results in one or more errors (depending on the query's errorPolicy
).
ApolloError
If the query produces one or more errors, this object contains either an array of graphQLErrors
or a single networkError
. Otherwise, this value is undefined
.
For more information, see Handling operation errors.
MaybeMasked<TData>
An object containing the result from the most recent previous execution of this query.
This value is undefined
if this is the query's first execution.
TVariables | undefined
An object containing the variables that were provided for the query.
boolean
If true
, the associated lazy query has been executed.
This field is only present on the result object returned by useLazyQuery
.
ApolloClient<any>
The instance of Apollo Client that executed the query. Can be useful for manually executing followup queries or writing data to the cache.
boolean
If true
, the query is still in flight and results have not yet been returned.
NetworkStatus
A number indicating the current network state of the query's associated request. See possible values.
Used in conjunction with the notifyOnNetworkStatusChange
option.
<TFetchData = TData, TFetchVars extends OperationVariables = TVariables>(fetchMoreOptions: FetchMoreQueryOptions<TFetchVars, TFetchData> & {
updateQuery?: (previousQueryResult: Unmasked<TData>, options: {
fetchMoreResult: Unmasked<TFetchData>;
variables: TFetchVars;
}) => Unmasked<TData>;
}) => Promise<ApolloQueryResult<MaybeMasked<TFetchData>>>
A function that helps you fetch the next set of results for a paginated list field.
(variables?: Partial<TVariables>) => Promise<ApolloQueryResult<MaybeMasked<TData>>>
A function that enables you to re-execute the query, optionally passing in new variables
.
To guarantee that the refetch performs a network request, its fetchPolicy
is set to network-only
(unless the original query's fetchPolicy
is no-cache
or cache-and-network
, which also guarantee a network request).
See also Refetching.
(pollInterval: number) => void
A function that instructs the query to begin re-executing at a specified interval (in milliseconds).
() => void
A function that instructs the query to stop polling after a previous call to startPolling
.
<TSubscriptionData = TData, TSubscriptionVariables extends OperationVariables = TVariables>(options: SubscribeToMoreOptions<TData, TSubscriptionVariables, TSubscriptionData>) => () => void
A function that enables you to execute a subscription, usually to subscribe to specific fields that were included in the query.
This function returns another function that you can call to terminate the subscription.
<TVars extends OperationVariables = TVariables>(mapFn: (previousQueryResult: Unmasked<TData>, options: Pick<WatchQueryOptions<TVars, TData>, "variables">) => Unmasked<TData>) => void
A function that enables you to update the query's cached result without executing a followup GraphQL operation.
See using updateQuery and updateFragment for additional information.
ObservableQuery<TData, TVariables>
A reference to the internal ObservableQuery
used by the hook.
ReadonlyArray<GraphQLFormattedError>
⚠️ Deprecated
This property will be removed in a future version of Apollo Client. Please use
error.graphQLErrors
instead.
Next steps
Now that you understand how to fetch data with the useQuery
hook, learn how to update your data with the useMutation
hook!
After that, learn about some other handy Apollo Client features:
Local state management: Learn how to query local data.
Pagination: Learn how to fetch data incrementally from list fields.