Hey y’all, I have some backend folks who were inte...
# apollo-kotlin
s
Hey y’all, I have some backend folks who were interested in seeing what they can do to run some graphql calls with their service being the client, and I urged them to give apollo-kotlin a try. In the discussion I’ve been having some questions that I don’t feel 100% confident answering, so if you could help me out that’s be absolutely great. The context is a spring-mvc backend, no coroutines usage anywhere atm, and no plans to adopt webflux since Netflix DGS that we’re using there really isn’t playing very well with it, it’s bitten us in one service where we gave it a try. • What’s the current best approach to work with apollo-kotlin when not using coroutines everywhere else? ◦ RX3 compatibility is an option, but really not a preferred one, not from me either tbh. ◦
runBocking
and go from there, which should probably be fine in the context of spring-mvc anyway I think, but I might need to look more into this one ◦
suspendCancellableCoroutine
then if we need to bridge the gap from corotuines -> blocking code so the other way around • The default HTTP client is OkHttp, I see that ktor is also something that can be used, maybe only in 4.x? Seen in a discussion here, don’t know more details. Is there a way to provide your own implementation completely anyway? And let them wire it up with the existing client (whatever is given to them by Spring there) they’re using anyway, just to do the HTTP part of this? • To make their life easier, should we perhaps look into using apollo-api only, and not bring in any of the rest of the library, so that we can get the super convenient parts of the type-safe auto-generated code but need to do the rest of the wiring ourselves? Have you had this discussion with anyone else before perhaps? What approach did they go with in the end?
This message here https://kotlinlang.slack.com/archives/C01A6KM1SBZ/p1692881834746549?thread_ts=1692881462.583479&cid=C01A6KM1SBZ makes me feel like going the apollo-api way is a pretty safe bet, but would require much more wiring up with everything else of course. Since we would be skipping a lot of what this library has to offer by skipping
ApolloClient
basically.
m
Excellent question! Looks like it's either
runBlocking{}
or
apollo-api
.
apollo-api
will give them more control at the cost of some features missing. But maybe they don't need those features? (BTW, there's a doc about using the models directly here)
The main thing that comes to mind is normalized cached but sounds like this is not as important in the backend?
s
Yeah they seem to want to avoid even OkHttp if that’s possible, so I feel like going with apollo-api is the level they want to drop down to. Just followed your doc there and yeah seems like doing smth like this should work
Copy code
fun asd() {
  val query: giraffe.ChargeHistoryQuery = ChargeHistoryQuery()
  val body: String = buildJsonString {
    query.composeJsonRequest(this, CustomScalarAdapters.Empty)
  }
  val httpResponse: Response = sendHttpRequestWithYourFavoriteHttpClient(body, "application/json")
  val jsonResponse: JsonReader = httpResponse.body!!.source().buffer.jsonReader()
  val result: ApolloResponse<ChargeHistoryQuery.Data> = query.parseJsonResponse(jsonResponse, CustomScalarAdapters.Empty)
  val data: ChargeHistoryQuery.Data = result.dataAssertNoErrors
}

// This would be quite similar with their own HttpClient I presume
fun sendHttpRequestWithYourFavoriteHttpClient(body: String, mediaType: String): Response {
  return OkHttpClient.Builder().build()
    .newCall(
      Request.Builder()
        .post(body.toRequestBody(mediaType.toMediaType()))
        .build()
    )
    .execute()
}
The only thing that is easy here that may not be for them is that OkHttp gives you an easy way to get the response into an okio buffer with
source().buffer
, which may not be as simple for whatever they use. And also no way to not bring in the okio dependency since apollo-api uses it. Which I don’t think would be an issue by itself I’ll see what they think though. It’s just that it may not be 100% intuitive to work with directly from personal experience, at least not the first time you encounter it and I don’t think they’ve used it in the past.
m
okio != okhttp though. okio is pretty smol
s
With all that said, making your own instance of HttpEngine doesn’t look that bad where you do this here with Ktor https://github.com/apollographql/apollo-kotlin/blob/main/libraries/apollo-runtime/[…]kotlin/com/apollographql/apollo3/network/http/KtorHttpEngine.kt It’s just that then they’re back in
ApolloClient
land, and therefore need to somehow work with coroutines, which I feel like might be too much for them 😅
Oh yeah yeah I am totally aware, and I will make sure to bring that point with them. I am only mentioning it since I remember the first time I used okio directly I was thoroughly confused on how to properly use it, with sources, buffers and so on. It was a bit foreign to me at first glance.
m
Yea, I think
apollo-api
is the easiest and less risky choice there, the dependencies are very minimal
gratitude thank you 1
Ah! gotcha about okio
There should be adapters to/from
InputStream
/`OutputStream`
👍 1
Copy code
inputStream.source().buffer()
outputStream.sink().buffer()
That should cover 90% of the use cases I think if they have input/output streams?
🦜 1
s
Yeah, and from buffer you do .jsonReader() on it and you’re back on apollo-api land, so sounds all good. I will mention this and see if they can get a hold of an input/output stream from their http client. Which I assume is always gonna be the case no matter which one they’re using.
👍 1
A more representative case for them would be this then
Copy code
... same code
val inputStream: InputStream = sendHttpRequestWithYourFavoriteHttpClient(body, "application/json")
val jsonResponse: JsonReader = inputStream.source().buffer().jsonReader()
... same code
where their impl will have a return type of InputStream which would be this impl with okhttp
Copy code
fun sendHttpRequestWithYourFavoriteHttpClient(body: String, mediaType: String): InputStream {
  return OkHttpClient.Builder().build()
    .newCall(
      Request.Builder()
        .post(body.toRequestBody(mediaType.toMediaType()))
        .build(),
    )
    .execute()
    .body!!
    .byteStream()
}
but the return type of InputStream is the important bit here
👍 1
In the same topic, but steering the conversation off a bit, I think I remember reading either here or on some doc that you wanted to improve the Java client experience a bit with 4.0. If I imagined this please excuse me 😵‍💫 If I am not imagining it though, and there’s already some prior discussion or doc on this I’d love to read it if you have a link. If not, then maybe my question is, is the thought of improving the coroutines interop situation something on your mind as well? RX is definitely something that especially non-android folks would never bring into their project if they had a choice I think 😄 Maybe something with CompleteableFuture is more natural for them and I think coroutines can interop pretty well with it right? And this isn’t even for a Java backend, it’s 100% Kotlin, it’s just that they don’t have coroutines, and I do agree that bringing in coroutines to a group of people who’ve never worked with them is not a simple task, there’s a lot of mistakes you’re gonna do starting off.
m
Doc for
apollo-runtime-java
is there. It's duplicating most of the runtime to not use coroutines
s
Oh I had no idea this existed 👀
m
We haven't communicated much around it yet 🙂
s
Yeah I understand, since it’s alpha and all.
👍 1
m
Right now, it doesn't have caching. I'm not sure if we will ever add it. Depends the feedbacks/priorization I guess
s
Right, so this would also be the perfect candidate for backend (and not only I guess) clients which simply do not want to work with coroutines. Is this idea becoming a reality because you’ve had many folks from Android who are still on Java and don’t want to depend on a Kotlin library, or for non-android folks? If it’s not for android maybe caching won’t even be that much of a requested feature. But I suppose it really depends on who the target audience is. Also since I know you haven’t communicated much around this runtime yet. What would you say is the state of it at this moment? Besides lacking caching support, should it at least in theory do everything else? With an unstable API of course since it’s Alpha, but still curious if I should try to convince my folks to give this a try now too, or if they should just stick with apollo-api instead.
m
It's mostly for non-Android folks so far, most of Android has made the switch to Kotlin and if not couroutines, have RxJava.
👍 1
Besides lacking caching support, should it at least in theory do everything else?
What I'm trying to figure out here is what else there is besides caching. At the end of the day, sounds like
apollo-runtime-java
=
apollo-api
+ OkHttp setup + Subscriptions/Websockets
Maybe interceptor APIs if they need it
s
Right yeah that might be all. Besides that, perhaps also allowing the users to provide their own Http client might be a highly requested feature if my colleagues end up being a good representative example for non-android users of this library. And if this does in fact end up being an important bit, then there may be a need for a core java runtime without the OkHttp dependency, and an extra module which provides a default OkHttp backed HttpEngine. For interceptor they can use the OkHttp one I assume for the default case, but yeah if they can use a different client they may need something there.
m
apollo-api
is the corner stone of Apollo and has a small API surface.
apollo-api
isn't going anywhere and allows to use your HTTP client of choice (because none is provided by default).
apollo-runtime-java
comes with a much bigger API footprint and is not battle tested at all. The good thing is that it has some resemblance to the Kotlin runtime but it might also be a bad thing because it's not 1:1 the same implementation. All in all, I'd say
apollo-api
is a safe bet. If they want to try new stuff and give us feedback, have them try
apollo-runtime-java
but this might change more than
apollo-api
s
Yeap apollo-api is probably what we’re gonna go with for now, I was now thinking more generally for what a typical user of apollo-runtime-java may be looking for. Sounds all good from my part, thanks a lot for this discussion and the help, I appreciate it!
thank you color 1
m
It's always a pleasure 🙂
🦠 1