GraphQL with Retrofit in Android
In this post, I'm going to explain how to do a Retrofit call with GraphQL in Android with Kotlin codebase.
implementation 'com.squareup.retrofit2:retrofit:2.9.0
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
2.- Create a DataConverter.kt and DataConverterFactory.kt to unwrap the nested JSON inside data that returns from GraphQL Responses.
DataConverter.kt
import okhttp3.ResponseBody
import retrofit2.Converter
class Data<T> {
var data: T? = null
}
class DataConverter<Any>(
private val delegate: Converter<ResponseBody, Data<Any>>?
) : Converter<ResponseBody, Any> {
override fun convert(value: ResponseBody): Any? {
try {
val graphQLDataModel = delegate?.convert(value)
return graphQLDataModel?.data
} catch (e: java.lang.Exception){
return null
}
}
}
DataConverterFactory.kt
import com.google.gson.reflect.TypeToke
import okhttp3.ResponseBody
import retrofit2.Converter
import retrofit2.Retrofit
import java.lang.reflect.Type
class DataConverterFactory : Converter.Factory() {
override fun responseBodyConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
return try {
val dataType = TypeToken.getParameterized(Data::class.java, type).type
val converter: Converter<ResponseBody, Data<Any>>? = retrofit.nextResponseBodyConverter(this, dataType, annotations)
DataConverter(converter)
} catch (e: Exception) {
null
}
}
}
3.- Create RetrofitService.kt and declare a method getData to get Graphql response.
interface RetrofitService {
@Headers("Content-Type: application/json"
@POST("/")
suspend fun getData(@Body body: JsonObject) : CustomObject)
}
4.- In the same RetrofitService.kt add the companion object for instance the Retrofit call.
Recommended by LinkedIn
What is a companion object in Kotlin?
In Kotlin, there is no direct support of the “static” keywords similar to other programming languages like C# or Java, so it’s primarily used to define class level variables and methods called static variables.
companion object {
private var retrofitService: RetrofitService? = null
private var gson = GsonBuilder()
.setLenient()
.create()
private val logging = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
private var client: OkHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(logging)
.build()
fun getInstance() : RetrofitService {
if (retrofitService == null) {
retrofitService = Retrofit.Builder()
.baseUrl("https://grapqhl.query.url")
.addConverterFactory(DataConverterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build().create(RetrofitService::class.java)
}
return retrofitService!!
}
}
5.- RetrofitService.kt with the whole implementation.
import com.androidsquad.countriesapp.model.Continent
import com.androidsquad.countriesapp.model.Country
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.*
interface RetrofitService {
@Headers("Content-Type: application/json"
@POST("/")
suspend fun getData(@Body body: JsonObject) : CustomObject)
companion object {
private var retrofitService: RetrofitService? = null
private var gson = GsonBuilder()
.setLenient()
.create()
private val logging = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
private var client: OkHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(logging)
.build()
fun getInstance() : RetrofitService {
if (retrofitService == null) {
retrofitService = Retrofit.Builder()
.baseUrl("https://grapqhl.query.url")
.addConverterFactory(DataConverterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build().create(RetrofitService::class.java)
}
return retrofitService!!
}
}
}
6.- Call RetrofitService.
val paramObject = JsonObject()
paramObject.addProperty("query", "your query")
val retrofitService = RetrofitService.getInstance()
val refrofitRespone = retrofitService.getData(paramObject)
Conclusion
Try to adapt GraphQL for using queries with Retrofit as you see in this post it's not easy to implement and take a lot of code, instead of it, I recommend to use Apollo Kotlin to use GraphQL with Android.
The reason behind it's because Apollo let us to handle the queries with a single line of code, and handle the data response, with Apollo for Android it's not necessary to add an extra implementation to extract the nested JSON that it's inside in the data object as we do with Retrofit .
Another tradeoff is the testing part, mocking this implementation could take some time to investigate the best way to make it easy and readable.