watchQuery
Fetch data from the GraphQL server then watch the cache for updates. Ideal for reactive components. Returns a non-terminating Observable.
API
Apollo.watchQuery<TData, TVariables, TErrorPolicy, TPartial>(
options: WatchQueryOptions<TData, TVariables, TErrorPolicy, TPartial>
): QueryObservable<TData, TVariables, WatchQueryStates<TPartial>, TErrorPolicy>
Returns a QueryObservable<TData, TVariables> which extends RxJS's standard Observable<QueryResult<TData>> and is a wrapper around Apollo Client's underlying ObservableQuery<TData, TVariables>.
This allows direct calls to Observable members like subscribe and pipe and also ObservableQuery members like variables, refetch, fetchMore and subscribeToMore...
For the complete list of options available, please refer to Apollo Client docs
Results support Apollo's data-state narrowing, including partial cache reads and incremental delivery. loading tracks pending work; dataState describes the available fields.
refetch, reobserve, and setVariables promises follow the query's errorPolicy, including cancellation. fetchMore defaults independently to none; pass its policy on the call itself. See default options and Apollo's error policies.
Apollo's event-based refetching works through refetchEventManager in withApolloOptions and the query's refetchOn option.
Executing a query
To execute a query within an Angular component, inject Apollo and pass it a GraphQL query document. When your component renders, watchQuery observable emits a QueryResult object that contains loading, error, data and previousData properties you can use to render your UI.
Let's look at an example.
First, we'll create a GraphQL query named Books:
fragment BookFragment on Book {
id
name
genre
authorId
}
query Books($name: String, $genre: String, $authorId: ID) {
books(name: $name, genre: $genre, authorId: $authorId) {
...BookFragment
}
}
Saving book.graphql will trigger codegen of BOOKS_QUERY typed gql document in /graphql/operations.ts file as per our setup.
For more information about codegen and how it works, please refer to the Codegen section.
It is recommended to define re-usable fragments, like BookFragment above, for a couple of reasons:
- Codegen will generate a
BookFragmenttype which can be consistently referenced throughout the codebase. - Using the same fragment in queries and mutations ensures that Apollo Client can normalize data in cache correctly. For example, if an
updateBookmutation returns aBookFragmentthen ourwatchQueryobservable will automatically emit the new value when the mutation executes successfully.
Next, we define the query in our component:
import { Apollo } from '@apollo-orbit/angular';
import { BOOKS_QUERY } from '../graphql';
@Component({
selector: 'app-books',
templateUrl: './books.component.html'
})
export class BooksComponent {
private readonly apollo = inject(Apollo);
protected readonly booksQuery = this.apollo.watchQuery({ query: BOOKS_QUERY });
protected refetch(): void {
this.booksQuery.refetch();
}
}
Then, we subscribe to the query using async pipe and handle the different query states:
<h3>Books</h3>
@if (booksQuery | async; as booksResult) {
@if (booksResult.loading) { Loading... }
@if (booksResult.error) { {{ booksResult.error.message }} }
@if (booksResult.data?.books; as books) {
@for (book of books; track book.id) {
<div>{{ book.name }}</div>
}
}
}
Variables
A variables object may be passed to watchQuery options:
import { BOOKS_QUERY } from '../graphql';
@Component({
...
})
export class BooksComponent {
protected readonly booksQuery = this.apollo.watchQuery({
query: BOOKS_QUERY,
variables: { genre: 'Fiction' }
});
}
In cases where the query has required variables, a compile time error will be thrown if the variables are not provided.
Refetching
Let's modify the previous example to allow users to manually refetch the data:
import { Apollo } from '@apollo-orbit/angular';
import { BOOKS_QUERY } from '../graphql';
@Component({
selector: 'app-books',
templateUrl: './books.component.html'
})
export class BooksComponent {
private readonly apollo = inject(Apollo);
protected readonly booksQuery = this.apollo.watchQuery({ query: BOOKS_QUERY });
protected refetch(): void {
this.booksQuery.refetch();
}
}
Then, we modify the template to include a button that calls refetch, and read (booksResult.data ?? booksResult.previousData)?.books.
When a new execution temporarily has no data, this keeps the last result visible until new data arrives.
Orbit exposes a previousData property which stores the last non-nil value of the data property that can be used to provide a smoother user experience.
<h3>
Books
<button type="button" (click)="refetch()">⟳</button>
</h3>
@if (booksQuery | async; as booksResult) {
@if (booksResult.loading) { Loading... }
@if (booksResult.error) { {{ booksResult.error.message }} }
@if (booksResult.data?.books; as books) {
@if ((booksResult.data ?? booksResult.previousData)?.books; as books) {
@for (book of books; track book.id) {
<div>{{ book.name }}</div>
}
}
}
query
Apollo.query<TData, TVariables, TErrorPolicy>(
options: QueryOptions<TData, TVariables, TErrorPolicy>
): Observable<SingleQueryResultForOptions<TData, TErrorPolicy>>;
Unlike watchQuery, query's observable terminates once the data is fetched.
The request starts when subscribed to and emits Apollo Client's single-query result: { data, error }, without loading, networkStatus, dataState or previousData. With errorPolicy: 'none' (the default), failures reach the observable's error channel. With all, a result can contain both data and an error; ignore omits the error and can return undefined data. Result types also account for declared defaults.
Use startWithLoading() when a template needs a watched-query-shaped result and an initial loading emission:
import { startWithLoading } from '@apollo-orbit/angular';
protected readonly books$ = this.apollo.query({ query: BOOKS_QUERY }).pipe(startWithLoading());
It is ideal for once off queries, like inside an Angular route guard, resolver or within a component method.
mapQuery
Orbit provides a mapQuery RxJS operator to map both data and previousData of a query result while preserving the other properties.
This is useful when you want to map the query result without dealing with the nullability of data and previousData properties.
import { mapQuery } from '@apollo-orbit/angular';
...
protected readonly bookNames$ = this.apollo.watchQuery({ query: BOOKS_QUERY }).pipe(
mapQuery(data => data.books.map(book => book.name))
);