15

I'm new to GraphQL but I have been using Retrofit for a while now and its easy to use and fast. GraphQL is much different than rest apis in terms of how you pass data. There really are not too many tutorials out there on using GraphQL with Android, I was only able to find this video (https://www.youtube.com/watch?v=P0uJHI7GjIc&t=1s) but there is no real code in there.

In my current code for retrofit calls I have an endpoint I set like so:

final RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(endPoint)
                .build();
        T service = restAdapter.create(clazz);

Then I call a rest service like this:

@GET("/users/{login}")
    Observable<Github> getUser(@Path("login") String login);

Now with GraphQL you only have a base url and no service path. Also if you are querying like userId=1 then you have to send as Post with Body parameters:

operationName: arbitrary name ,
query: "{users(userid:$userId){username}}, 
variables: "{"userId":1}"

I'm just not sure how this translates to Retrofit

Mike6679
  • 5,547
  • 19
  • 63
  • 108
  • 2
    You would need to create a JSON body containing your `query`, `operationName`, and `variables`, then arrange for Retrofit to POST that JSON to the server, to its designated GraphQL URL. I am unconvinced that Retrofit will be of much benefit over just using OkHttp and Gson for GraphQL. – CommonsWare Nov 29 '16 at 21:25
  • Ok I see here how to create a JSON body for retrofit : http://stackoverflow.com/questions/21398598/how-to-post-raw-whole-json-in-the-body-of-a-retrofit-request. But what should the Post path be ? i.e: I only have a base url for my Graphql server. – Mike6679 Nov 29 '16 at 21:57
  • 2
    "But what should the Post path be" -- a GraphQL server only has one endpoint, usually. So, use the base URL that you already have, particularly if it ends in `/graphql`. – CommonsWare Nov 29 '16 at 22:19
  • I ended up just using OkHttp --- just much easier in this case. – Mike6679 Nov 30 '16 at 02:55

3 Answers3

13

Building Queries & Parsing responses for GraphQL is not straightforward. If you are using this on pet-projects, i recommend to start exploring Apollo. This client is under serious development but you can already have a look and play with it.

https://github.com/apollographql/apollo-android

I enjoyed working with Apollo so far, and there are nice features on their roadmap: RxJava Integration, Retrofit, Subscriptions and support for AutoValue

Entreco
  • 12,738
  • 8
  • 75
  • 95
9

In your manifest to add

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

Your dependencies

// Kotlin Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'

//OkHttp
implementation ("com.squareup.okhttp3:okhttp:3.12.12"){
  force = true //API 19 support
}
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.12'

//retrofit
implementation "com.squareup.retrofit2:retrofit:2.7.1"
implementation "com.squareup.retrofit2:converter-scalars:$2.7.1"

Also Java 8 compatibility

android {

    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

With the service

import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST

interface GraphQLService {

    @Headers("Content-Type: application/json")
    @POST("/")
    suspend fun postDynamicQuery(@Body body: String): Response<String>
}

you can create a object

import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory

object GraphQLInstance {

    private const val BASE_URL: String = "http://192.155.1.55:2000/"

    val graphQLService: GraphQLService by lazy {
        Retrofit
            .Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(ScalarsConverterFactory.create())
            .build().create(GraphQLService::class.java)
    }
}

In the activity you can create this method

private fun post(userId: String){
    val retrofit = GraphQLInstance.graphQLService
    val paramObject = JSONObject()
    paramObject.put("query", "query {users(userid:$userId){username}}")
    GlobalScope.launch {
        try {
            val response = retrofit.postDynamicQuery(paramObject.toString())
            Log.e("response", response.body().toString())
        }catch (e: java.lang.Exception){
            e.printStackTrace()
        }
    }
}

You can check the example in GitHub and my post

Note: if you need a mutation should be to change this line

paramObject.put("query", "query {users(userid:$userId){username}}")

to

paramObject.put("query", "mutation {users(userid:$userId){username}}")
Cabezas
  • 9,329
  • 7
  • 67
  • 69
  • 2
    I'm always getting status code 400 >> Must provide query string. ,any help please? – Amer Hadi Nov 14 '21 at 14:02
  • 1
    Can you check your query in your playground graphQL? Maybe you can find the error. – Cabezas Nov 16 '21 at 09:02
  • getting (data=null) , any help pelase – Aashutosh Jan 29 '23 at 10:08
  • I get error: Invalid JSON payload in GraphQLAuth POST request. A curl request works. On Android, the log shows a lots of backslashes for the payload Eg: "{\"query\":\"query MyQuery {requestOTP(deviceId:\\\"1cb57166271668b1\\\", mobilePhone:\\\"0423130842\\\") {message}}\"}". – Rowan Gontier May 22 '23 at 08:05
0

This approach worked for me:

interface ApiService {
    @POST("myUrl")
    @Headers("Content-Type: application/json")
    suspend fun requestOtp(@Body requestData: GraphQlRequest): MyResponse
}

data class GraphQlRequest(
    val query: String,
    val variables: Map<String, Any>
)

val query = "query MyQuery { requestOTP(mobilePhone: \"0423130812\" ) { message } }"
val variables = emptyMap<String, Any>()

val request = GraphQlRequest(query, variables)
val apiService = retrofit.create(MyApiService::class.java)
val response = apiService.executeGraphQlQuery(request)
Rowan Gontier
  • 821
  • 9
  • 14