🌐 Building a Compose Multiplatform GitHub GraphQL Client with Apollo Kotlin

Have you ever wanted to harness the power of GraphQL in a Compose Multiplatform app? In this guide, I’ll walk you through how to build a GitHub GraphQL client using Apollo Kotlin in a Compose Multiplatform setup.
🧰 Prerequisites
- IntelliJ IDEA or Android Studio (latest)
- Basic knowledge of Compose Multiplatform and Kotlin
- GitHub personal access token (for GitHub GraphQL API)
⚙️ Step 1: Install Apollo GraphQL Plugin
Install the Apollo GraphQL plugin:
- Go to Settings → Plugins
- Search for “Apollo GraphQL”
- Install and restart the IDE
📦 Step 2: Define Apollo Dependencies
In your libs.versions.toml
:
[versions]
apollo = "4.1.1"
[libraries]
apollo-runtime = { module = "com.apollographql.apollo3:apollo-runtime", version.ref = "apollo" }
apollo-normalized-cache = { module = "com.apollographql.apollo3:apollo-normalized-cache", version.ref = "apollo" }
apollo-normalized-cache-sqlite = { module = "com.apollographql.apollo3:apollo-normalized-cache-sqlite", version.ref = "apollo" }
🏗️ Step 3: Set Up Gradle Plugins and Source Sets
In your app/build.gradle.kts
:
plugins {
alias(libs.plugins.apollo)
}
apollo {
service("GithubGraphQLApi") {
// GraphQL configuration here.
// https://www.apollographql.com/docs/kotlin/advanced/plugin-configuration/
packageName.set("com.droidslife.graphqldemo.graphql")
introspection {
endpointUrl.set("https://api.github.com/graphql")
schemaFile.set(file("src/commonMain/graphql/schema.graphqls"))
}
}
}
Ensure GraphQL queries are placed under src/commonMain/graphql
.
🔽 Step 4: Download GitHub GraphQL Schema
Use either method:
- Plugin: Tools → Apollo → Download Schema
- Terminal:
./gradlew :app:downloadServiceApolloSchemaFromIntrospection \
--endpoint="https://api.github.com/graphql" \
--header="Authorization: Bearer YOUR_GITHUB_TOKEN"
🧠 Step 5: Create Apollo Client And interceptor
Create ApolloClientCache.kt
:
class ApolloClientCache : KoinComponent {
private val mutex = Mutex()
private val interceptor: MyApolloInterceptor by inject()
private var apolloClient: ApolloClient? = null
// GitHub GraphQL API endpoint
private val githubGraphQLEndpoint = "https://api.github.com/graphql"
/**
* Gets the Apollo client instance, creating it if it doesn't exist.
* Thread-safe using a mutex to prevent multiple instances from being created.
*/
suspend fun getApolloClient(): ApolloClient {
if (apolloClient == null) {
mutex.withLock {
if (apolloClient == null) {
apolloClient = createApolloClient()
}
}
}
return apolloClient!!
}
/**
* Creates a new Apollo client configured for the GitHub GraphQL API.
*/
private fun createApolloClient(): ApolloClient {
return ApolloClient.Builder()
.networkTransport(
HttpNetworkTransport.Builder()
.serverUrl(githubGraphQLEndpoint)
.build()
)
.normalizedCache(
MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024) // 10MB cache
)
.addInterceptor(interceptor)
.build()
}
}
Create Interceptor to add token MyApolloInterceptor.kt
:
class MyApolloInterceptor : ApolloInterceptor, KoinComponent {
// Use the GitHub token from BuildConfig
// This token is set during build time from the environment variable
private val githubToken: String = BuildConfig.GITHUB_TOKEN
override fun <D : Operation.Data> intercept(
request: ApolloRequest<D>,
chain: ApolloInterceptorChain
): Flow<ApolloResponse<D>> = flow {
// Create a new request with the Authorization header
val authorizedRequest = request.newBuilder()
.addHttpHeader("Authorization", "Bearer $githubToken")
.build()
// Proceed with the modified request
emitAll(chain.proceed(authorizedRequest))
}
}
🔍 Step 6: Define Your GraphQL Query
Create SearchRepositoriesQuery.graphql
under src/commonMain/graphql
:
# Query to search for repositories
query SearchRepositoriesQuery($query: String!, $first: Int = 20) {
search(query: $query, type: REPOSITORY, first: $first) {
repositoryCount
edges {
node {
... on Repository {
id
name
nameWithOwner
description
url
stargazerCount
forkCount
owner {
login
avatarUrl
}
primaryLanguage {
name
color
}
}
}
}
}
}
🗂️ Step 7: Create a Repository to Execute Query
Now create a Repository to execute above GraphQL Query GitHubRepositoryImpl.kt
:
class GitHubRepositoryImpl(
private val apolloClientCache: ApolloClientCache
) : GitHubRepositoryInterface {
override fun searchRepositories(query: String): Flow<Result<List<GitHubRepository>>> = flow {
try {
val apolloClient = apolloClientCache.getApolloClient()
val response = apolloClient.query(SearchRepositoriesQuery(query = query)).execute()
if (response.hasErrors()) {
val errorMessage = response.errors?.firstOrNull()?.message ?: "Unknown error occurred"
emit(Result.failure(Exception(errorMessage)))
} else {
// Map the GraphQL response to our GitHubRepository data class
val repositories = response.data?.search?.edges?.mapNotNull { edge ->
edge?.node?.onRepository?.let { repo ->
GitHubRepository(
name = repo.name,
owner = repo.owner.login,
description = repo.description,
stars = repo.stargazerCount,
forks = repo.forkCount,
language = repo.primaryLanguage?.name,
release = repo.latestRelease?.releaseFragment?.toReleaseDetail()
)
}
} ?: emptyList()
emit(Result.success(repositories))
}
} catch (e: Exception) {
emit(Result.failure(e))
}
}
}
🖼️ Step 8: Build the UI with Compose Multiplatform
Use Compose Multiplatform to create a simple UI that:
- Accepts a query
- Displays a list of repositories with stars, owner, and description
🤝 Let’s Talk!
Have you integrated GraphQL in your Compose Multiplatform project? What were your challenges and learnings?
Drop a ⭐ on the repo if it helped, and feel free to contribute!
🔗 Source Code: GitHub GraphQL Demo App