Pride in Your App - Trying Out GraphQL on Android

Reading time: about 6 minutes

Published

Pride in Your App - Trying Out GraphQL on Android

It's Pride month, y'all! As someone who is part of the LGBTIQA+ community, this month is both great and stressful at the same time. You never know what some people come up with - somehow, this month draws some really nasty people out. And with the rise of far-right in Europe... I'm not even going to get started.

I have wanted to try out GraphQL with Android for quite a while, and when I came across this Pride Flag API, I thought that now was the time. What a great way to celebrate Pride by trying out the API (and GraphQL) and writing a blog post about it!

A bit of background about me and GraphQL: I started my public speaking career talking about GraphQL (and JavaScript. That's my dark secret.). It was fun, and I liked it a lot. Of course, there are always pros and cons when thinking about using any technology, but nevertheless, I liked it a lot. I even got this "Ask me about GraphQL" T-shirt from one meetup, and that was probably the coolest t-shirt I've gotten.

When I switched to Android, there were no use cases for me to learn how to integrate GraphQL with Android. So, I've wanted to try it for a while now, and I'm glad I found a great way to test it. Let's get started!

GraphQL

If you've never heard the word GraphQL, it's a query language and an alternative way of building APIs in the form of graphs - hence the name.

GraphQL APIs let you ask for what you need - and only that. So, compared to REST APIs, which always return everything, GraphQL APIs return only the values you've asked for. And as it's graph-based, you can get nested data with one query. An example would be authors, their books, and the characters in the books - with REST API, you'd need to make three requests (depending on the API, naturally).

GraphQL offers three actions: querying, mutating, or subscribing to data. Compared to REST, querying matches the GET requests, mutating matches all the others that mutate data, and subscribing is a two-way street - like, for example, WebSockets.

In the context of this blog post, we're going to query data. To learn more about the operations available and GraphQL in general, head over to GraphQL's documentation.

To use GraphQL, we're going to use Apollo Kotlin (formerly Apollo Android). The library is type-safe and compatible with Kotlin Multiplatform.

Dependencies and Getting the Schema

To get started with Apollo Kotlin, we need to add some dependencies to the project:

// project-level build.gradle.kts 

plugins {
  id("com.apollographql.apollo3").version("3.8.4")
}

and

// module-level build.gradle.kts 

dependencies {
  implementation("com.apollographql.apollo3:apollo-runtime:3.8.4")
}

Note that the version numbers for these packages must be the same.

In addition to dependencies, we need to add a package name addition task for the generated models:

// module-level build.gradle.kts 

apollo { 
    service("service") { 
        packageName.set("com.example") 
    } 
}

With many projects, syncing gradle files would happen right here. However, this project is different; we still need the schema for the GraphQL queries. There are several options, but with an external API, downloading the schema with introspection is the easiest. We need to save the schema to src/main/graphql, and in the case of this small project, we do it with the following command:

./gradlew :app:downloadApolloSchema --endpoint='https://pride.dev/api/graphql' --schema=app/src/main/graphql/schema.graphqls

Once we have the schema, it's time to sync the gradle files and continue to querying the data.

Querying Data

For this small app, we want to read the data - so, in GraphQL's terms, we want to query it. We don't need every property for the flags, so we define a query with just what we need:

query FlagQuery {
  allFlags {
    name
    year
    svgUrl
  }
}

With Apollo Kotlin, all queries need to be named queries. You'll run into an error if you have an unnamed query - trust me, I know.

This query gets all the available flags from the API. We need the flag's name, year, and SVG URL, so we write those properties in our query.

To add the query to the Android project, we first need to create a file in the src/main/ folder, which is the very same folder where the schema lives. Let's call the file FlagQuery.graphql and add the query there. Apollo generates the model for the query through an automated task that runs when the app is built. So, let's build the app, and then query's model will be available.

At this point, I want to remind you to add the internet permission to AndroidManifest. If you don't, well, the following steps will become more complicated. Trust me, I know.

Here's a reminder on what to add, so you don't need to use a search engine to find it, like I did:

// AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

Okay, now we have the query, but how do we connect it to the UI? The answer is a GraphQL client, which we can use to query the data. I'm making things straightforward for the purposes of this demo, so we only define a GraphQL client and then query the data in a view model. In a production-grade app, you'd probably have a more layered architecture.

First, we define the client:

// GraphQLClient.kt

const val API_ENDPOINT = "https://pride.dev/api/graphql"

val apolloClient = ApolloClient.Builder()
    .serverUrl(API_ENDPOINT)
    .build()

In the view model, we first define a state to use in the UI:

// FlagsViewModel.kt

data class FlagsUiState(
    val loading: Boolean = true,
    val flags: List<FlagQuery.AllFlag> = emptyList(),
    val currentFlag: FlagQuery.AllFlag? = null,
)

class FlagsViewModel : ViewModel() {
    private var _state = MutableStateFlow(FlagsUiState())
    val state = _state.asStateFlow()
    ...
}

We want to store all the flags we get from the API and display the current flag on a detail page, so we have properties for flags and currentFlag in the state. The type (FlagQuery.AllFlag) for both is generated by Apollo Kotlin.

To query all the flags, let's define a function inside the view model:

// FlagsViewModel.kt

suspend fun getFlags() {
    val flagsQuery = apolloClient
        .query(FlagQuery())
        .execute()

    _state.update { currentState ->
        currentState.copy(
            loading = false,
            flags = flagsQuery.data?.allFlags ?: emptyList(),
        )
    }
}

So, to get the flags, we need a suspend function, inside which we call the apolloClient.query(...).execute() with the FlagQuery we defined previously. On success, it returns data in the data property, which we can then use to update the state with.

I mentioned that we also want to store the current flag in the state. Setting it is a straightforward function without any GraphQL-magic:

// FlagsViewModel.kt

fun setFlag(flag: FlagQuery.AllFlag) {
    _state.update {
        it.copy(
            currentFlag = flag,
        )
    }
}

A Couple of Words About the UI

On the UI side, we're doing pretty simple things: displaying a list of the flags and then navigating to a detail view. As it's not related to GraphQL, meaning it's just passing data as state, it's out of the scope of this blog post.

Here's a video of how the experience looks like:

If you're interested in checking the UI code out, head over to the repository: Flag Explorer

Wrapping Up

In this blog post, we discussed GraphQL and how to use it in an Android project. As an example, we built a small app that fetches Pride flags from the Pride Flag API.

In the end, adding GraphQL to the project was surprisingly straightforward. For some reason, I had thought it'd be much more complicated. As mentioned in the beginning, I have warm feelings towards GraphQL, so it'd be great to use it in some real projects. We'll see what the future brings!

What do you think of GraphQL? Have you tried it out?

Links in the Blog Post