# How GraphQL Queries Are Used Alongside REST API Calls in GSY GitHub App Compose

> Discover how GSY GitHub App Compose uses GraphQL queries with Apollo Client and REST API calls via Retrofit for efficient data fetching and compatibility.

- Repository: [Shuyu Guo/gsygithubappcompose](https://github.com/carguo/gsygithubappcompose)
- Tags: deep-dive
- Published: 2026-02-26

---

**The GSY GitHub App Compose project strategically combines Apollo Client for GraphQL queries to fetch complex repository details with a Retrofit-based REST client for list operations, enabling efficient data fetching while maintaining compatibility with GitHub's existing REST infrastructure.**

The `carguo/gsygithubappcompose` repository demonstrates a pragmatic approach to modern Android networking by implementing both protocols simultaneously. This open-source GitHub client uses **GraphQL queries alongside REST API calls** to leverage the strengths of each technology, optimizing for performance where it matters while preserving simplicity for standard CRUD operations.

## Architecture Overview

The codebase maintains two distinct networking layers that coexist through dependency injection, allowing repository classes to choose the appropriate protocol for each specific use case.

### GraphQL Layer with Apollo Client

In [`core/network/src/main/java/com/shuyu/gsygithubappcompose/core/network/graphql/GraphQLProvider.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/core/network/src/main/java/com/shuyu/gsygithubappcompose/core/network/graphql/GraphQLProvider.kt), the application constructs a singleton **Apollo Client** configured for GitHub's GraphQL endpoint:

```kotlin
@Module
@InstallIn(SingletonComponent::class)
object GraphQLProvider {
    @Provides @Singleton
    fun provideApolloClient(tokenInterceptor: TokenInterceptor): ApolloClient {
        val logging = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        val client = OkHttpClient.Builder()
            .addInterceptor(logging)
            .addInterceptor(tokenInterceptor)
            .build()

        return ApolloClient.Builder()
            .serverUrl(NetworkConfig.BASE_GRAPHQL_URL) // https://api.github.com/graphql
            .okHttpClient(client)
            .build()
    }
}

```

The [`GraphQLService.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/GraphQLService.kt) file provides thin wrapper functions around this client, exposing methods like `getRepository` that execute specific queries:

```kotlin
class GraphQLService @Inject constructor(
    private val apolloClient: ApolloClient
) {
    suspend fun getRepository(owner: String, name: String) =
        apolloClient.query(GetRepositoryDetailQuery(owner, name)).execute()
}

```

### REST Layer with Retrofit

Concurrently, [`core/network/src/main/java/com/shuyu/gsygithubappcompose/core/network/di/NetworkModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/core/network/src/main/java/com/shuyu/gsygithubappcompose/core/network/di/NetworkModule.kt) provisions a **Retrofit 2** instance for traditional REST operations:

```kotlin
@Provides @Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
    .baseUrl(NetworkConfig.BASE_URL)               // https://api.github.com/
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

@Provides @Singleton
fun provideGitHubApiService(retrofit: Retrofit): GitHubApiService =
    retrofit.create(GitHubApiService::class.java)

```

The [`GitHubApiService.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/GitHubApiService.kt) interface defines standard REST endpoints including `searchRepositories`, `getUser`, and `starRepository`, covering the majority of GitHub's public API surface.

## Strategic Convergence in Repository Details

The repository-detail flow exemplifies how **GraphQL queries complement REST API calls** within the same data layer. In [`data/src/main/java/com/shuyu/gsygithubappcompose/data/repository/RepositoryRepository.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/data/src/main/java/com/shuyu/gsygithubappcompose/data/repository/RepositoryRepository.kt), the application fetches detailed repository information using GraphQL while maintaining REST for other operations:

```kotlin
fun getRepositoryDetail(owner: String, name: String) = flow {
    // Try DB cache first (unified approach)
    val cached = repositoryDetailDao.getRepositoryDetail("$owner/$name")
    if (cached != null) emit(RepositoryResult(Result.success(cached.toRepositoryDetailModel()), DataSource.CACHE, false))

    // GraphQL call for fresh, rich data
    val response = apolloClient.query(GetRepositoryDetailQuery(owner, name)).execute()
    response.data?.repository?.let {
        repositoryDetailDao.insert(it.toEntity())
        emit(RepositoryResult(Result.success(it.toEntity().toRepositoryDetailModel()), DataSource.NETWORK, true))
    } ?: emit(RepositoryResult(Result.failure(Throwable("Repository null")), DataSource.NETWORK, true))
}

```

This approach fetches nested repository data—including fields from multiple sub-objects—in a single request. While the same data could be retrieved via the REST endpoint `GET repos/{owner}/{repo}`, GraphQL is preferred here for its ability to return precisely the fields needed without multiple round trips.

Conversely, data-heavy list operations use REST exclusively:

```kotlin
// Trending repositories via REST
val response = apiService.searchRepositories(query, page = page)
repositoryDao.clearAndInsert(response.items.map { it.toEntity() })

```

## Why Mix GraphQL and REST?

The hybrid architecture in `gsygithubappcompose` follows four strategic principles:

**Selective Richness** — GraphQL is employed where a single call replaces multiple REST requests, such as repository details requiring nested contributor, language, and release data.

**Legacy Compatibility** — Most GitHub public APIs remain REST-based. The existing `GitHubApiService` covers search, events, commits, issues, stars, and forks without requiring complex GraphQL query definitions.

**Unified Caching** — Both protocols feed into the same **Room** database cache. GraphQL responses transform into entities via `repository.toEntity()` and persist identically to REST responses, ensuring consistent offline behavior.

**Gradual Migration** — The mixed approach allows incremental GraphQL adoption without rewriting the entire networking stack, reducing risk while modernizing specific data flows.

## Implementation Examples

### Configuring the Apollo Client

The [`GraphQLProvider.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/GraphQLProvider.kt) file centralizes GraphQL configuration, attaching authentication interceptors shared with the REST client:

```kotlin
return ApolloClient.Builder()
    .serverUrl(NetworkConfig.BASE_GRAPHQL_URL)
    .okHttpClient(client) // Reuses OkHttp configuration
    .build()

```

### GraphQL Service Abstraction

The `GraphQLService` class encapsulates query execution logic, providing type-safe suspend functions that repository classes consume:

```kotlin
class GraphQLService @Inject constructor(
    private val apolloClient: ApolloClient
) {
    suspend fun getRepository(owner: String, name: String) =
        apolloClient.query(GetRepositoryDetailQuery(owner, name)).execute()
        
    suspend fun getTrendUser() =
        apolloClient.query(GetTrendUserQuery()).execute()
}

```

### Retrofit REST Setup

The [`NetworkModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/NetworkModule.kt) configures **OkHttp** interceptors for logging and authentication, then binds them to Retrofit:

```kotlin
@Provides @Singleton
fun provideOkHttpClient(tokenInterceptor: TokenInterceptor): OkHttpClient {
    return OkHttpClient.Builder()
        .addInterceptor(HttpLoggingInterceptor().apply { 
            level = HttpLoggingInterceptor.Level.BODY 
        })
        .addInterceptor(tokenInterceptor)
        .build()
}

```

### Unified Repository Pattern

Repository classes in [`RepositoryRepository.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/RepositoryRepository.kt) inject both services and select protocols based on data requirements:

- **GraphQL**: Complex objects with nested relationships (repository details)
- **REST**: List endpoints, pagination, and simple CRUD (trending repos, user events, stars)

## Summary

- The `gsygithubappcompose` project implements **Apollo Client** for GraphQL and **Retrofit** for REST within the same codebase.
- **GraphQL queries** handle complex repository detail fetching in [`RepositoryRepository.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/RepositoryRepository.kt), reducing round trips for nested data.
- **REST API calls** manage list operations, search, and standard GitHub endpoints through [`GitHubApiService.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/GitHubApiService.kt).
- Both protocols share **OkHttp** interceptors for authentication and logging, and both persist data to **Room** for unified offline caching.
- This hybrid approach maximizes data efficiency for specific use cases while maintaining compatibility with GitHub's extensive REST infrastructure.

## Frequently Asked Questions

### Why doesn't the app migrate completely to GraphQL?

The GitHub REST API remains the primary interface for most operations including search, events, and pagination. According to the source code, maintaining REST for these endpoints avoids the complexity of rewriting working code, while GraphQL is adopted selectively where it provides measurable performance benefits for complex data fetching.

### How does the app maintain consistent authentication across both protocols?

Both the `ApolloClient` in [`GraphQLProvider.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/GraphQLProvider.kt) and the `OkHttpClient` in [`NetworkModule.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/NetworkModule.kt) share the same `TokenInterceptor`. This interceptor attaches the OAuth token to headers regardless of whether the request originates from a GraphQL query or REST call, ensuring seamless authentication across the hybrid stack.

### Which specific operations use GraphQL versus REST?

The [`RepositoryRepository.kt`](https://github.com/carguo/gsygithubappcompose/blob/main/RepositoryRepository.kt) file shows that **GraphQL** handles repository detail queries via `GetRepositoryDetailQuery`, while **REST** manages trending repositories (`searchRepositories`), user profiles, starred repos, and issue lists. The decision depends on whether the operation requires fetching nested relationships (GraphQL) or simple list pagination (REST).

### How does the caching layer handle mixed protocol responses?

Both GraphQL and REST responses transform into identical Room entities before storage. For example, repository details fetched via GraphQL convert using `repository.toEntity()` and insert into `repositoryDetailDao`, just as REST responses do. This creates a unified cache layer that abstracts the data source from the UI layer.