Querying the Github GraphQL API with Kotlin

We are going to show how to query a GraphQL API using the Apollo GraphQL Android Client. It is not Android only and can be used by any Java/Kotlin project. We are going to use this client with Kotlin to show how to get started! For this, we are going to use a popular GraphQL endpoint that is publicly available, namely the Github API.

Configuring the project

Before we can start, we are going to set up a project with the correct dependencies. We will set up a Gradle project with the following build.gradle.kts file. We have to add the apollo plugin since we want to use it as a client. Furthermore, we add some dependencies for coroutines. Finally, we configure the plugin to generate Kotlin models.

group = "nl.bartvwezel"
repositories {
    mavenCentral()
}

plugins {
    kotlin("jvm") version "1.4.0"
    // plugin for generating the models from the schema
    id("com.apollographql.apollo").version("2.4.0")
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
    // The core runtime dependencies
    implementation("com.apollographql.apollo:apollo-runtime:2.4.0")
    // Coroutines extensions for easier asynchronicity handling
    implementation("com.apollographql.apollo:apollo-coroutines-support:2.4.0")
}

// configure the plugin to generate Kotlin models, default is Java
apollo {
    generateKotlinModels.set(true)
}
// set Java version and compatibility to 8 for compatibility with Kotlin
java.sourceCompatibility = JavaVersion.VERSION_1_8
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Another thing we have to do during setup is creating a personal access token, so that we can https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token

Here you should set the following permissions:

user
public_repo
repo
repo_deployment
repo:status
read:repo_hook
read:org
read:public_key
read:gpg_key

Finally, we have to download the schema.json from the GraphQL API we are going to use. The Apollo plugin uses this schema to generate the Kotlin Model. We can do this with the new task provided by the Apollo plugin:

./gradlew downloadApolloSchema \
  --endpoint="https://api.github.com/graphql" \
  --schema="src/main/graphql/schema/github/schema.json" \
  --header="Authorization: Bearer $TOKEN"

Querying

Let’s start with a simple query. This query will retrieve your own username ( and is the default query on the explorer) :

query retrieveUsername {
    viewer {
        login
    }
}

To generate models for this query, we are going to add it to the same folder as the schema.json. In our case this is the folder: src/main/graphql/schema/github/. When we rebuild the project, the models will be created. This means we can finally start with executing the query! For this, we have to instantiate an Apollo Client. We can create the query by creating a new RetrieveUsernameQuery. To execute the query we will enqueue and wait to get the result, which we print to the terminal.

suspend fun main() {
    // set your own token
    val token = ""
    val okHttpClient = OkHttpClient.Builder()
            .addInterceptor { chain: Interceptor.Chain ->
                val original: Request = chain.request()
                val builder: Request.Builder = original.newBuilder().method(original.method(), original.body())
                builder.header("Authorization", "bearer $token")
                chain.proceed(builder.build())
            }
            .build()

    val apolloClient: ApolloClient = ApolloClient.builder()
            .serverUrl("https://api.github.com/graphql")
            .okHttpClient(okHttpClient)
            .build()
    
    val user = apolloClient.query(RetrieveUsernameQuery()).await()
    print(user.data?.viewer?.login)
}

That was a quite simple query, let’s do something more complicated. We are going to query your own repositories. For this we are including the paging options by the API. The first step is the same, lets add the following query to right folder:

query retrieveRepositories($first:Int!,  $before:String, $after:String) { 
  viewer { 
    repositories(first: $first, before: $before, after: $after){
      pageInfo {
        startCursor
        endCursor
        hasNextPage
        hasPreviousPage
      }
      nodes{
        name
      }
    }
  }
}

To use this query with variables, we have to rerun the build to generate the models. After that we can do almost the same as the previous query except that we can pass variables to the constructor of the query:

suspend fun main() {
    // set your own token
    val token = ""
    val okHttpClient: OkHttpClient = OkHttpClient.Builder()
            .addInterceptor { chain: Interceptor.Chain ->
                val original: Request = chain.request()
                val builder: Request.Builder = original.newBuilder().method(original.method(), original.body())
                builder.header("Authorization", "bearer $token")
                chain.proceed(builder.build())
            }
            .build()

    val apolloClient: ApolloClient = ApolloClient.builder()
            .serverUrl("https://api.github.com/graphql")
            .okHttpClient(okHttpClient)
            .build()

    val user = apolloClient.query(RetrieveRepositoriesQuery(first = 10)).await()
    print(user.data?.viewer?.repositories)
}

Mutations

Now let’s try a mutation, the idea is the same. The mutation we are going to demonstrate will create a new issue in one of our repositories. We create a mutation and put it in the same folder as the schema:

mutation CreateIssue($createIssueInput:CreateIssueInput!){
    createIssue(input: $createIssueInput) {
        clientMutationId
    }
}

We rerun a build so that we have a new model. Then we can use the Apollo Client to perform the mutation. Before we can perform the mutation we retrieve a project, where we are going to create an issue. To create the mutation, we first construct a new CreateIssueInput. The generator automatically detects what we need here, which is a project ID and a title.

suspend fun main() {
    // set your own token
    val token = ""
    val okHttpClient: OkHttpClient = OkHttpClient.Builder()
            .addInterceptor { chain: Interceptor.Chain ->
                val original: Request = chain.request()
                val builder: Request.Builder = original.newBuilder().method(original.method(), original.body())
                builder.header("Authorization", "bearer $token")
                chain.proceed(builder.build())
            }
            .build()

    val apolloClient: ApolloClient = ApolloClient.builder()
            .serverUrl("https://api.github.com/graphql")
            .okHttpClient(okHttpClient)
            .build()

    val repositories = apolloClient.query(RetrieveRepositoriesQuery(first = 30)).await()
    val repoId = repositories.data!!.viewer.repositories.nodes?.first()!!.id
    val name = repositories.data!!.viewer.repositories.nodes?.first()!!.name
    print(name);
    val mutation = CreateIssueMutation(createIssueInput = CreateIssueInput(repositoryId = repoId, title = "New Issue"))
    apolloClient.mutate(mutation).await()
}

For more information about the Github API, you can find the docs here! If you want to know more about Apollo GraphQL Client, you can find the documentation here! Finally, if you want to see the code, check out the repository. Thanks for reading and feel free to ask questions.

Leave a Reply