Exploring GraphQL with Coroutines on Android

E

In a recent article of mine we took a look at how we could implement OAuth flows in Android with the use of Custom Tabs . This was done using the Product Hunt API and now that we authenticated users within our client, we can go ahead and interact with the rest of the API. The authentication flow for Product Hunt is operated as a REST API – when it comes to the rest of the API though, this is using GraphQL. This means that for our Android client, we need to handle the communication with this part of the API in a different manner than how we did within the authentication flow.

In this post we’re going to look at setting up our project for use with GraphQL, along with learning how we can perform queries. If you are not familiar with what GraphQL is, it would be worth checking out the project as we’re not going to cover these things in the scope of this article.


When it comes to communicating with the ProductHunt API, for the scope of this article we’re going to be performing a query to retrieve the currently authenticated user – which feels like a natural task after having performed the authentication flow in the previous article.

To configure our project to be able to communicate with the API, we’re going to need to add a dependency which will help us with this process. For this we’ll be using the apollo graphql client for Android, we’ll need to add a few things to our project before we can use this. First of all we’ll need to add the following to our collection of repositories for our project within our project level build.gradle file:

repositories {
    maven {
        url "https://dl.bintray.com/apollographql/android"
    }
}

Within that same file we’ll need to add a dependency for the apollo gradle plugin:

dependencies {
    classpath 'com.apollographql.apollo:apollo-gradle-plugin:1.2.2'
}

Next we’ll need to hop on over to the build.gradle file for the module that is going to be using the graphql client. Here we want to add the graphql plugin:

apply plugin: 'com.apollographql.android'

Followed by adding the required dependencies for apollo. We’ll start with the required runtime dependency, along with the one that will give us access to coroutines support:

implementation 'com.apollographql.apollo:apollo-runtime:1.2.2'
implementation 
        'com.apollographql.apollo:apollo-coroutines-support:1.2.2'

At this stage we have all of the external dependencies added to our project for apollo, but with the way that Graphql works there are a couple more things that we need to do to correctly configure them for our project.

The dependency that we added for Graphql is going perform code generation that will take our declared queries and validate/generate them using the API schema provided by us. If you aren’t too familiar with graphql at this point, there are a couple of concepts here that will help us out to be aware of:

  • API schema – for every graphql API there is a schema which is essentially a contract for what is contained and available from the API. A copy of this schema needs to be added to the project so that our queries can be validated against it
  • API queries – for each operation that we want to perform with the API we need to define a query. This query will then be used in conjunction with the provided schema to generate a query builder for use by us within the code of our project

With these files that we’re going to add to our project, we need to provide a place for them to live. Within the module of where graphql is going to be used, we’re going to add a new directory which will be located at main/graphql – this is because the code generation process will use this directory location to access the schema and queries that we have defined. In your project this will look something similar to this:


The first thing we’re going to add to this directory is the schema for our API. This is going to look very different for each project, but essentially it is a json file which acts as a contract for the API – for producthunt the schema looks like this. For the API you’re working with they will provide access to a copy of their scheme, you will need to copy this .json file into the graphql directory that we’ve created, as shown above. Using this schema, we’re now going to write our first query. Because we’ve authenticated the user, we’re now going to want to retrieve the data for this user from the API. I’m going to create a new file (GetAuthenticatedViewer.graphql) with the following content.

Note: It’s important to notice the .graphql extension on the end of the file, whilst this just looks like a text file to us, the code generation will look for these files with the extensions.

query Viewer {
    viewer {
        user {
            id
            coverImage
            headline
            isMaker
            isViewer
            profileImage
            username
            name
        }
    }
}

In the concept of the product hunt API, the viewer is the currently authenticated user. When retrieving a viewer, the user instance for that viewer is returned as a nested object. Let’s break this query down a bit so that we can see what each part is doing!

We start by defining that we are writing a query that is going to request data from the api, the data that is fetched will depend on the constraints that we declare in our query.

query Viewer {

}

Next we declare what we are going to be requesting within our query, so here we are going to say that we want to retrieve the viewer (based on the access token that is being sent as a header) and then also the user that is nested inside of that viewer. A viewer also contains other data object such as goals and makerGroups, but we don’t care for those right now – emitting them from our query means that they wont be returned.

viewer {
    user {

    }
}

Finally, we declare the data that we want to come back for our user reference. Again, the user contains more data than we are requesting here but we don’t need all of it, so omitting the data we don’t need means that it won’t be returned.

id
coverImage
headline
isMaker
isViewer
profileImage
username
name

For examples sake, we may sometimes need to send query parameters for the query that we are performing. In these cases we can pass in these parameters as outlined below. here we are using the id reference to declare that we want to fetch the user where the given id matches the id of the user object. This is an ‘allowed’ query as defined within the schema file, so you won’t be able query against the field of an object unless it is defined within that contract.

query User($id: ID!) {
    user(id: $id) {
        id
        coverImage
        headline
        isMaker
        isViewer
        profileImage
        username
        name
    }
}

So now that we have our query defined, we can go ahead and build our project – at this point apollo should generate the required class for our query. We can jump into the build directory of our module and navigate through to the directory that our queries are defined in – at this point we should be able to see our generated query files.

This generated class contains a builder which allows us to programatically build our query for retrieving the current viewer, which will look like so:

val viewerQuery = ViewerQuery
    .builder()
    .build()

This example is a basic query so it doesn’t make the builder look too useful as it is. In the case of retrieving the user by a given ID, we would chain each query parameter onto the builder for the query. The query User($id: ID!) example that we looked at above would look like the below:

val userQuery = UserQuery
    .builder()
    .id(userId)
    .build()

When our query is executed we will be returned a model that represents the defined query. So for example, the ViewQuery will contain a Viewer object, with a nested User object inside of it that we can retrieve and make use of within our code.


Now that we have our queries defined, we’re going to want to execute these so that we can actually retrieve the response for them. For this we’re going to need to build an instance of an ApolloClient that will be used to coordinate our network requests.

fun makeApiClient(token: String): ApolloClient {
    return ApolloClient.builder()
        .serverUrl("https://api.producthunt.com/v2/api/graphql")
        .okHttpClient(makeHttpClient(token))
        .build();
}

Here we create a new reference to an ApolloClient builder, assign it a base URL to be used for our requests and then attach an http client using OkHttp (the code for which can be seen below, it’s likely similar to what you might already be using in your android projects). Now that we have this ApolloClient reference, we can pass this into whatever class will be used to make our API requests.

private fun makeHttpClient(token: String): OkHttpClient {
    return OkHttpClient.Builder()
        .addNetworkInterceptor {
            val original = it.request()
            val originalHttpUrl = original.url

            val url = originalHttpUrl.newBuilder()
                .addQueryParameter("access_token", token)
                .build()

            val requestBuilder = original.newBuilder().url(url)
            val request = requestBuilder.build()
                it.proceed(request)
            }
        .build()
}

In my project I’m using a class called UserRemoteStore which is backed by an interface used for handling user-related operations on the API. My ApolloClient reference is passed into this class as a constructor argument and within this class I have a function named retrieveAuthenticatedUser(). In this call we retrieve the currently authenticated user, which is retrieved using the current access token for the client.

When we use this query() function we are provided an instance of a ApolloQueryCall. This class handles the execution of our GraphQL query call, returning us the object in the form of what our query defines. At this point, we would previously have used enqueue() to execute our request and assign a callback for the result state. However, with the addition of the graphql coroutines dependency we’re going to make this call coroutines friendly 😍

Using the toDeferred() function on our ApolloCall reference will convert it to a Deferred reference. With this in place we can now use await() to wait for the execution of this call to complete, assigning the result in our userData variable reference.

suspend fun retrieveAuthenticatedUser(): UserModel {
    val viewerQuery = ViewerQuery
        .builder()
        .build()
    try {
        val userData = 
            apolloClient.query(viewerQuery).toDeferred().await()
        return userData.data()?.viewer()?.user()?.let { viewer ->
            UserModel.fromViewer(viewer)
        } ?: run {
            throw IllegalStateException(...)
        }
    } catch (apollo: ApolloException) {
        throw NetworkException(...)
    }
}

We start here by using the ViewerQuery which was generated from our defined Graphql query. Using our ApolloClient reference we can trigger a query call using the query() function, passing in a reference to the query that we wish to be executed.

Once this request has been executed and we have the result, we’re going to pick into our userData variable (which is the response returned from our query) and retrieve the user reference that is nested inside of it. If this isn’t null then we return some response, otherwise throw an exception which will be handle down the chain. For the success state I’m using an extension function to map the user model to a model representation used in the other layers of my application.

With this retrieveAuthenticatedUser() suspending function now fully implemented, we can call this from within our application to retrieve the currently authenticated user to handle from within our application.


In this article we’ve taken look at how we can integrate our GraphQL setup with coroutines, either allowing us to tie this in with existing approaches in our applications, or introduce the addition of coroutines for an improved approach to asynchronous operations. Whilst we’ve only skimmed the surface of how we can interact with GraphQL with coroutines, this should set you up to explore the combination of the two further!

I’ll be implementing more GraphQL in my Felix project, which will all be available on the open-source github project. In the meantime, if you have any questions on GraphQL and/or how to integrate coroutines with it, please do reach out!

[twitter-follow screen_name=’hitherejoe’ show_count=’yes’]

About the author

hitherejoe

Add Comment

By hitherejoe