Android GraphQL API Integration with Apito - GraphQL Tools & Database API

Home/Documentation/Android GraphQL API Integration with Apito - GraphQL Tools & Database API

Android GraphQL API Integration with Apito

Learn how to integrate Apito’s GraphQL tools and database API with your Android applications using Apollo Android. This instant API builder provides powerful GraphQL endpoints and build API tool features optimized for mobile development.

Overview

Apito’s GraphQL tools and database API offer seamless integration with Android applications through Apollo Android client. This instant API platform generates type-safe Kotlin/Java models from your GraphQL schema, ensuring excellent performance and developer experience on mobile devices.

Prerequisites

  • Android Studio Arctic Fox or later
  • Kotlin 1.5+ or Java 8+
  • Minimum SDK 21 (Android 5.0)
  • An Apito project with GraphQL API enabled

Installation

Add Apollo Android Dependencies

Add the following dependencies to your app-level build.gradle file:

dependencies {
    implementation 'com.apollographql.apollo3:apollo-runtime:3.8.2'
    implementation 'com.apollographql.apollo3:apollo-android-support:3.8.2'
    
    // For coroutines support
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
}

Apply Apollo Plugin

Add the Apollo plugin to your app-level build.gradle:

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.apollographql.apollo3' version '3.8.2'
}

Configuration

Download GraphQL Schema

  1. Navigate to your Apito Console
  2. Go to API Explorer section
  3. Click on GraphQL tab
  4. Download the schema file as schema.graphqls
  5. Place it in src/main/graphql/ directory

Configure Apollo

Create an apollo configuration block in your build.gradle:

apollo {
    service("apito") {
        packageName.set("com.yourapp.graphql")
        schemaFile.set(file("src/main/graphql/schema.graphqls"))
    }
}

Implementation

Initialize Apollo Client

Create an Apollo client in your Application class or dependency injection module:

import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.network.okHttpClient
import okhttp3.Interceptor
import okhttp3.OkHttpClient

class ApitoGraphQLClient {
    companion object {
        private const val BASE_URL = "https://api.apito.io/secured/graphql"
        private const val API_KEY = "your-api-key-here"
        
        val apolloClient: ApolloClient by lazy {
            val okHttpClient = OkHttpClient.Builder()
                .addInterceptor { chain ->
                    val request = chain.request().newBuilder()
                        .addHeader("X-API-KEY", API_KEY)
                        .addHeader("Content-Type", "application/json")
                        .build()
                    chain.proceed(request)
                }
                .build()
                
            ApolloClient.Builder()
                .serverUrl(BASE_URL)
                .okHttpClient(okHttpClient)
                .build()
        }
    }
}

Create GraphQL Queries

Create GraphQL query files in src/main/graphql/:

GetProducts.graphql:

query GetProducts($limit: Int, $offset: Int) {
  products(limit: $limit, offset: $offset) {
    id
    title
    description
    price
    image {
      url
    }
    categories {
      id
      name
    }
  }
}

CreateProduct.graphql:

mutation CreateProduct($input: ProductInput!) {
  createProduct(input: $input) {
    id
    title
    description
    price
    createdAt
  }
}

Usage in Activities/Fragments

Fetching Data with Coroutines

import com.apollographql.apollo3.exception.ApolloException
import kotlinx.coroutines.launch
import androidx.lifecycle.lifecycleScope

class ProductActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_product)
        
        fetchProducts()
    }
    
    private fun fetchProducts() {
        lifecycleScope.launch {
            try {
                val response = ApitoGraphQLClient.apolloClient
                    .query(GetProductsQuery(limit = 10, offset = 0))
                    .execute()
                    
                if (response.hasErrors()) {
                    // Handle GraphQL errors
                    response.errors?.forEach { error ->
                        Log.e("GraphQL", "Error: ${error.message}")
                    }
                } else {
                    // Handle successful response
                    response.data?.products?.let { products ->
                        displayProducts(products)
                    }
                }
            } catch (e: ApolloException) {
                Log.e("Apollo", "Network error: ${e.message}")
            }
        }
    }
    
    private fun displayProducts(products: List<GetProductsQuery.Product>) {
        products.forEach { product ->
            Log.d("Product", "Title: ${product.title}, Price: ${product.price}")
        }
        // Update your RecyclerView or UI components
    }
}

Creating Data with Mutations

private fun createProduct(title: String, description: String, price: Double) {
    lifecycleScope.launch {
        try {
            val input = ProductInput(
                title = title,
                description = description,
                price = price
            )
            
            val response = ApitoGraphQLClient.apolloClient
                .mutation(CreateProductMutation(input))
                .execute()
                
            if (response.hasErrors()) {
                // Handle errors
                showError("Failed to create product")
            } else {
                // Handle success
                response.data?.createProduct?.let { product ->
                    showSuccess("Product created with ID: ${product.id}")
                }
            }
        } catch (e: ApolloException) {
            showError("Network error: ${e.message}")
        }
    }
}

Repository Pattern Implementation

For better architecture, implement the Repository pattern:

import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ProductRepository @Inject constructor(
    private val apolloClient: ApolloClient
) {
    
    suspend fun getProducts(limit: Int, offset: Int): Result<List<Product>> {
        return try {
            val response = apolloClient
                .query(GetProductsQuery(limit, offset))
                .execute()
                
            if (response.hasErrors()) {
                Result.failure(Exception("GraphQL errors: ${response.errors}"))
            } else {
                val products = response.data?.products?.map { it.toProduct() } ?: emptyList()
                Result.success(products)
            }
        } catch (e: ApolloException) {
            Result.failure(e)
        }
    }
    
    suspend fun createProduct(productInput: ProductInput): Result<Product> {
        return try {
            val response = apolloClient
                .mutation(CreateProductMutation(productInput))
                .execute()
                
            if (response.hasErrors()) {
                Result.failure(Exception("GraphQL errors: ${response.errors}"))
            } else {
                response.data?.createProduct?.let { product ->
                    Result.success(product.toProduct())
                } ?: Result.failure(Exception("No data received"))
            }
        } catch (e: ApolloException) {
            Result.failure(e)
        }
    }
}

// Extension function to map GraphQL types to domain models
private fun GetProductsQuery.Product.toProduct(): Product {
    return Product(
        id = id,
        title = title,
        description = description,
        price = price,
        imageUrl = image?.url
    )
}

ViewModel Integration

Use with Android Architecture Components:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class ProductViewModel @Inject constructor(
    private val productRepository: ProductRepository
) : ViewModel() {
    
    private val _products = MutableStateFlow<List<Product>>(emptyList())
    val products: StateFlow<List<Product>> = _products
    
    private val _loading = MutableStateFlow(false)
    val loading: StateFlow<Boolean> = _loading
    
    private val _error = MutableStateFlow<String?>(null)
    val error: StateFlow<String?> = _error
    
    fun loadProducts() {
        viewModelScope.launch {
            _loading.value = true
            _error.value = null
            
            productRepository.getProducts(limit = 20, offset = 0)
                .onSuccess { products ->
                    _products.value = products
                }
                .onFailure { exception ->
                    _error.value = exception.message
                }
            
            _loading.value = false
        }
    }
    
    fun createProduct(title: String, description: String, price: Double) {
        viewModelScope.launch {
            val input = ProductInput(title, description, price)
            
            productRepository.createProduct(input)
                .onSuccess {
                    loadProducts() // Refresh the list
                }
                .onFailure { exception ->
                    _error.value = exception.message
                }
        }
    }
}

Error Handling

Implement comprehensive error handling:

sealed class GraphQLResult<T> {
    data class Success<T>(val data: T) : GraphQLResult<T>()
    data class Error<T>(val message: String, val errors: List<com.apollographql.apollo3.api.Error>? = null) : GraphQLResult<T>()
    data class NetworkError<T>(val exception: ApolloException) : GraphQLResult<T>()
}

suspend fun <T> executeGraphQLQuery(
    query: suspend () -> com.apollographql.apollo3.api.ApolloResponse<T>
): GraphQLResult<T> {
    return try {
        val response = query()
        
        if (response.hasErrors()) {
            GraphQLResult.Error(
                message = "GraphQL errors occurred",
                errors = response.errors
            )
        } else {
            response.data?.let { data ->
                GraphQLResult.Success(data)
            } ?: GraphQLResult.Error("No data received")
        }
    } catch (e: ApolloException) {
        GraphQLResult.NetworkError(e)
    }
}

Best Practices

1. Caching Strategy

// Enable normalized caching
val apolloClient = ApolloClient.Builder()
    .serverUrl(BASE_URL)
    .normalizedCache(
        MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024) // 10MB
    )
    .build()

2. Network Optimization

  • Use appropriate cache policies
  • Implement proper retry mechanisms
  • Handle offline scenarios gracefully

3. Security

  • Store API keys securely using Android Keystore
  • Implement certificate pinning for production
  • Validate all user inputs before mutations

Conclusion

Integrating Apito’s GraphQL API with Android applications provides a robust, type-safe solution for mobile data management. This open source API builder offers excellent performance and developer experience for Android development.

The combination of Apito’s open source CMS and Apollo Android ensures scalable, maintainable mobile applications with powerful GraphQL integration.

Next Steps