Queries
Fetching data in a simple, predictable way is one of the core features of Apollo Android. In this guide, you'll learn how to Query GraphQL data and use the result in your android application. You'll also learn how Apollo-android Client simplifies your data management code by tracking different error 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 queries are just standard GraphQL, anything you can type into the GraphiQL query explorer can also be put into .graphql
files in your project.
The following examples assume that you've already set up Apollo Android for your Android/Java application. Read our getting started guide if you need help with either of those steps.
All code snippets are taken from the apollo-sample project and can be found here.
Apollo-android takes a schema and a set of .graphql
files and uses these to generate code you can use to execute queries and access typed results.
All
.graphql
files in your project (or the subset you specify as input toapollo-codegen
if you customize the script you define as the code generation build phase) will be combined and treated as one big GraphQL document. That means fragments defined in one.graphql
file are available to all other.graphql
files for example, but it also means operation names and fragment names have to be unique and you will receive validation errors if they are not.
Creating queries
Queries are represented as instances of generated classes conforming to the GraphQLQuery
protocol. Constructor arguments can be used to define query variables if needed.
You pass a query object to ApolloClient#query(query)
to send the query to the server, execute it, and receive results.
For example, if you define a query called FeedQuery
:
1query FeedQuery($type: FeedType!, $limit: Int!) {
2 feedEntries: feed(type: $type, limit: $limit) {
3 id
4 repository {
5 name
6 full_name
7 owner {
8 login
9 }
10 }
11 postedBy {
12 login
13 }
14 }
15}
Here, query
is the operation type and FeedQuery
is the operation name.
Apollo-android will generate a FeedQuery
class that you can construct (with variables) and pass to ApolloClient#query(query)
:
1apolloClient().query(feedQuery)
2 .enqueue(object: ApolloCall.Callback<FeedQuery.Data>() {
3 override fun onResponse(response: Response<UpvotePost.Data>) {
4 Log.i(TAG, response.toString());
5 }
6
7 override fun onFailure(e: ApolloException) {
8 Log.e(TAG, e.getMessage(), e);
9 }
10 }, uiHandler);
1apolloClient().query(feedQuery)
2 .enqueue(new ApolloCallback<>(new ApolloCall.Callback<FeedQuery.Data>() {
3 @Override public void onResponse(@NotNull Response<FeedQuery.Data> response) {
4 Log.i(TAG, response.toString());
5 }
6
7 @Override public void onFailure(@NotNull ApolloException e) {
8 Log.e(TAG, e.getMessage(), e);
9 }
10 }, uiHandler));
By default, Apollo will deliver query results on a background thread. You can provide a handler in
enqueue
, or useapollo-android-support
if you're using the result to update the UI.
The ApolloCall.Callback
also provides error handling methods for request parsing failed, network error and request cancelled, amongst others.
In addition to the data
property, response
contains an errors
list with GraphQL errors (for more on this, see the sections on error handling and the response format in the GraphQL specification).
Typed query results
Query results are defined as nested immutable classes that at each level only contain the properties defined in the corresponding part of the query definition. This means the type system won't allow you to access fields that are not actually fetched by the query, even if they are part of the schema.
For example, given the following schema:
1enum Episode { NEWHOPE, EMPIRE, JEDI }
2
3interface Character {
4 id: String!
5 name: String!
6 friends: [Character]
7 appearsIn: [Episode]!
8 }
9
10 type Human implements Character {
11 id: String!
12 name: String!
13 friends: [Character]
14 appearsIn: [Episode]!
15 height(unit: LengthUnit = METER): Float
16 }
17
18 type Droid implements Character {
19 id: String!
20 name: String!
21 friends: [Character]
22 appearsIn: [Episode]!
23 primaryFunction: String
24}
And the following query:
1query HeroAndFriendsNames($episode: Episode) {
2 hero(episode: $episode) {
3 name
4 friends {
5 name
6 }
7 }
8}
Apollo Android will generate a typesafe model looking like this (details are omitted to focus on the class structure):
1class HeroAndFriendsNamesQuery {
2 data class Data(val hero: Hero)
3 data class Hero(val name: String, friends: List<Friend>)
4 data class Friend(val name: String)
5}
1class HeroAndFriendsNamesQuery {
2 class Data {
3 Hero hero();
4 }
5 class Hero {
6 String name;
7 List<Friend> friends;
8 }
9 class Friend {
10 String name;
11 }
12}
You can fetch results data using the following code. Apollo will parse the response and expose a typesafe model:
1val heroAndFriendsQuery = HeroAndFriendsNames(episode = NEWHOPE)
2
3apolloClient().query(heroAndFriendsQuery)
4 .enqueue(object: ApolloCall.Callback<HeroAndFriendsNames.Data>() {
5 override fun onResponse(response: Response<HeroAndFriendsNames.Data>) {
6 Log.i(TAG, response.data?.hero?.name);
7 }
8
9 override fun onFailure(e: ApolloException) {
10 Log.e(TAG, e.getMessage(), e);
11 }
12 }, uiHandler);
13}
1final HeroAndFriendsNames heroAndFriendsQuery = HeroAndFriendsNames.builder()
2 .episode(NEWHOPE)
3 .build();
4
5apolloClient().query(heroAndFriendsQuery)
6 .enqueue(new ApolloCallback<>(new ApolloCall.Callback<HeroAndFriendsNames.Data>() {
7 @Override public void onResponse(@NotNull Response<HeroAndFriendsNames.Data> response) {
8 Log.i(TAG, response.data().hero().name();
9 }
10
11 @Override public void onFailure(@NotNull ApolloException e) {
12 Log.e(TAG, e.getMessage(), e);
13 }
14 }, uiHandler));
15}
Because the above query won't fetch appearsIn
, this property is not part of the returned result type and cannot be accessed here.