Apollo and RxJava: Android GraphQL the Right Way

aeldo

Akash Eldo

Posted on July 25, 2021

Apollo and RxJava: Android GraphQL the Right Way

Introduction

Since its inception, GraphQL has grown to become a very popular framework for building backend APIs. However, I still don’t see it used that often on Android. I think the reason is that there aren’t many resources on using GraphQL on Android, and what does exist is outdated or doesn’t take advantage of new technologies. In this article, I want to present a “best practices” approach to setting up a GraphQL client on Android. We’ll make a simple Android app that uses the SpaceX GraphQL API to display a list of rockets. By taking advantage of Apollo Android and RxJava, we’ll do GraphQL on Android the right way.

Setup

Before we get started, we have to get all our dependencies ready. First, paste the following into your project level build.gradle:

buildscript {
    repositories {
      ...
    }
    dependencies {
        classpath "com.apollographql.apollo:apollo-gradle-plugin:2.4.1"
        ...
   }
}

allprojects {
    repositories {
        ...
        // RxJava repo
        maven { url "https://oss.jfrog.org/libs-snapshot" }
    }
}
Enter fullscreen mode Exit fullscreen mode

This will add the apollo plugin and a maven repo we need tor RxJava. Next, add these dependencies and plugin to your app level build.gradle:

dependencies {
    ...
    // Apollo
    implementation "com.apollographql.apollo:apollo-runtime:2.4.1"
    implementation "com.apollographql.apollo:apollo-rx3-support:2.4.1"

    // RxJava
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
}

apply plugin: "com.apollographql.apollo"
Enter fullscreen mode Exit fullscreen mode

Here we add two Apollo related dependencies, one for the library itself and another for Rx support. We also add RxAndroid, an Android port of RxJava, and RxJava itself. We add RxJava explicitly because RxAndroid isn’t updated as often, so defining it manually lets us stay up to date with bug fixes. Don’t forget to add apply plugin: “com.apollographql.apollo” at the end of your gradle file, otherwise you’ll run into compiler issues later.

Writing GraphQL Queries

Create the directory src/main/graphql/com/example, but replace example with your own package name. After that, run the following command to fetch a GraphQL schema:

./gradlew downloadApolloSchema 
--endpoint="https://api.spacex.land/graphql/" 
--schema="src/main/graphql/com/example/schema.json"
Enter fullscreen mode Exit fullscreen mode

Replace ./gradlew with gradlew.bat if you're on Windows.

This command will run an introspection on the SpaceX API and create a schema.json for us. Next we'll write our GraphQL query, which we’ll use to fetch a list of rockets. Create a file called rockets.graphql in the same directory as schema.json and paste the following:

query RocketsQuery {
    rockets {
        id
        name
        description
    }
}
Enter fullscreen mode Exit fullscreen mode

This is a very basic GraphQL query that targets the rockets endpoint and fetches a list of rockets, with each entry containing the rocket's id, name, and description. The API actually provides a lot more information than this, but we only need these three data points. Since GraphQL lets us fetch only what we need, we don't have to slow down our requests with unnecessary information.‌ If you want to learn more about GraphQL in particular, this article is great place to start.

Now that we wrote our query, build the project. Apollo will read the graphql files we created and automatically generate classes that we’ll use to make queries against the server. If you set your project view to Android, you should see the following:

1_uHDE8Zo5AsLlPnJoAWqGIQ

Making a GraphQL Request

Now that we have all our GraphQL stuff setup, we can start writing some Java code. It’s good practice to keep all our server related code in one class, which we’ll call Server. Here's the shell of that class:

class Server {
    private static ApolloClient apolloClient;

    private static ApolloClient getApolloClient() {
        if (apolloClient == null) {
            //Build the Apollo Client
            String serverUrl = "https://api.spacex.land/graphql/";
            OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
            apolloClient = ApolloClient.builder()
                    .serverUrl(serverUrl)
                    .okHttpClient(okHttpClient)
                    .build();
        }
        return apolloClient;
    }
}
Enter fullscreen mode Exit fullscreen mode

This class uses the singleton pattern to create an ApolloClient instance. We'll use this helper method in our queries so we don't have to create a client with every query.

Next let’s make a method for our rockets query:

public static Observable<Response<RocketsQuery.Data>> fetchRockets() {
    ApolloQueryCall<RocketsQuery.Data> call = getApolloClient()
            .query(new RocketsQuery());

    return Rx3Apollo.from(call)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .filter((dataResponse -> dataResponse.getData() != null));
}
Enter fullscreen mode Exit fullscreen mode

This code is pretty dense, so let’s go through it slowly. First we create an ApolloQueryCall object with RocketsQuery, the class that was generated from the GraphQL file we wrote earlier. This object defines the GraphQL query we want to make against the SpaceX API.‌

We then use the helper class Rx3Apollo to convert the call into an RxJava object, which lets us do all our Rx magic on it. subscribeOn specifies that we want this request to be run on a background thread. With observeOn, we say that we want to handle the response on the main thread because we'll update the UI using the response. Finally, we add a filter to toss out null responses to prevent any null pointer exceptions.

This method returns an Observable that we can then use in our Activity code to handle loading and error states and update the UI when we get a response.

Now we’ll use the fetchRockets method we created to populate a list:

 // Show loading
ProgressBar progressBar = findViewById(R.id.main_progressBar);
progressBar.setVisibility(View.VISIBLE);

Server.fetchRockets().subscribeWith(new DisposableObserver<Response<RocketsQuery.Data>>() {
    @Override
    public void onNext(@NonNull Response<RocketsQuery.Data> dataResponse) {
        if (dataResponse.getData() != null) {
            RocketsAdapter adapter = new RocketsAdapter(dataResponse.getData().rockets(),MainActivity.this);
            recyclerView.setAdapter(adapter);
        }
    }

    @Override
    public void onError(@NonNull Throwable e) {

    }

    @Override
    public void onComplete() {
         // Query's done, so hide loading
         progressBar.setVisibility(View.GONE);
    }
});
Enter fullscreen mode Exit fullscreen mode

In this code snippet, we first show the loading indicator, because we’re about to fetch our data. We then use our Server class to do a rockets query and create a DisposableObserver to handle the result. This is one of the benefits of using RxJava with Apollo Android. DisposableObserver has three methods we can use to handle the result: onNext, onError, and onComplete. onNext is called when a valid response is received, and onError is called if an error occurs. Regardless of the outcome, onComplete is called once the request is completed. This lets us define our loading, error, and data states separately, which greatly simplifies our code. In this snippet, we attach the rockets to a custom adapter(code here) and then hide the loading indicator. Notice that the loading part is handled in onComplete, because we want to hide it even if an error occurs.

Conclusion

In this article we went through the right way to use GraphQL on Android by taking advantage of the Apollo Android and RxJava libraries. However, the example we went through is one the simplest request scenarios. For more advanced scenarios like parallel and dependent requests, check out this article’s companion repo. You can also read my next article, where we’ll take a closer look at some of the advanced examples in this repo.

As always, thanks for reading and let me know in the comments if this helped you out!

💖 💪 🙅 🚩
aeldo
Akash Eldo

Posted on July 25, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related