I would like to handle the threading of queries myself, but the only option I currently see for executing a query is enqueue which requires a callback. Instead of calling enqueue with a callback I would like to call something like execute which would block the current thread and return the Response or throw an ApolloException.
That api would not work due to apollo having cached responses and returning first cached and then network response.
I solved this with a synchronous coroutine in kotlin: https://medium.com/@andrea.bresolin/i-think-there-is-a-solution-for-this-1b2b02378876
Thanks for your blog post! That solution works well.
package main
import com.apollographql.apollo.ApolloCall
import com.apollographql.apollo.ApolloClient
import com.apollographql.apollo.api.Response
import com.apollographql.apollo.exception.ApolloException
import com.apollographql.apollo.github.FetchQuery
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
suspend fun callGithub(): Response<FetchQuery.Data> {
return suspendCoroutine { continuation ->
client.query(query).enqueue(
object : ApolloCall.Callback<FetchQuery.Data>() {
override fun onFailure(e: ApolloException) {
continuation.resumeWithException(e)
}
override fun onResponse(response: Response<FetchQuery.Data>) {
continuation.resume(response)
}
}
)
}
}
val client = ApolloClient.builder()
.serverUrl("https://api.github.com/graphql")
.build()!!
val query = FetchQuery()
fun main() = runBlocking {
println("graphql started!")
val data = callGithub()
println("graphql finished!")
}
Here's an extension function implementation:
suspend fun <T> ApolloCall<T>.execute() = suspendCoroutine<Response<T>> { continuation ->
enqueue(object : ApolloCall.Callback<T>() {
override fun onResponse(response: Response<T>) {
continuation.resume(response)
}
override fun onFailure(error: ApolloException) {
continuation.resumeWithException(error)
}
})
}
That's awesome! It feels like the sync API is back. 馃槃
Anyone has solution for java?
Here's my solution with projectreactor:
CompletableFuture<T> future = new CompletableFuture<>();
client.query(operation).enqueue(new ApolloCall.Callback<T>() {
@Override
public void onResponse(@NotNull Response<T> response) {
if (response.hasErrors()) {
String errors = response.errors().stream().map(Object::toString).collect(Collectors.joining(", "));
future.completeExceptionally(new ApolloException(errors));
return;
}
future.complete(response.data());
}
@Override
public void onFailure(@NotNull ApolloException e) {
future.completeExceptionally(e);
}
});
return Mono.fromFuture(future).block();
static <T extends com.apollographql.apollo.api.Query, V extends Operation.Data> V execute(Query operation) {
CompletableFuture<T> future = new CompletableFuture<>();
apolloClient.query(operation).enqueue(new Callback<T>() {
@Override
public void onResponse(@NotNull com.apollographql.apollo.api.Response<T> response) {
if (response.hasErrors()) {
String errors = response.getErrors().stream().map(Object::toString).collect(Collectors.joining(", "));
future.completeExceptionally(new ApolloException(errors));
return;
}
future.complete(response.getData());
}
@Override
public void onFailure(@NotNull ApolloException e) {
future.completeExceptionally(e);
}
});
return (V) Mono.fromFuture(future).block();
}
I added the method signature to the above and the cast required to get just the Operation.Data
So I can call:
SimpleSearchQuery.Data data = execute(simpleSearchQuery);
Is there something better than the above available these days?
Is there any Reactive native java client available that is able to leverage all the boilerplate from the ApolloGraphQL plugin and without dealing with the callback hell of the above?
@sbilello there is RxJava2 & Coroutines support:
Given https://www.apollographql.com/docs/android/advanced/rxjava2/
I wrote this small snippet:
static <T extends com.apollographql.apollo.api.Query, V extends Operation.Data> Optional<V> execute(Query operation) {
ApolloCall<V> apolloCall = apolloClient.query(operation);
return Rx2Apollo.from(apolloCall)
.map(r -> Optional.of(r.getData()))
.onErrorReturn(o -> {
logger.error(o.getMessage());
return Optional.empty();
})
.blockingFirst();
}
Given https://www.apollographql.com/docs/android/advanced/rxjava2/
I wrote this small snippet:static <T extends com.apollographql.apollo.api.Query, V extends Operation.Data> Optional<V> execute(Query operation) { ApolloCall<V> apolloCall = apolloClient.query(operation); return Rx2Apollo.from(apolloCall) .map(r -> Optional.of(r.getData())) .onErrorReturn(o -> { logger.error(o.getMessage()); return Optional.empty(); }) .blockingFirst(); }
I tried this but on the mapping part (.map(r -> Optional.of(r.getData())) ...) I noticed a problem. First of all, the method I only can use is "data()" because the class of r is Response and the class Response doesnt have a getData() method.
Second, even if I change it to data(), all I am getting is this error message:
"java.util.Optional cannot be cast to com.example.graphql.client.KundeQuery$Data"
I desperately require assistance because I just cant manage to archive data synchronously in graphql and nobody on stack overflow seems to be capable to help on such a specific topic.
My code for comparison:
public static <T extends com.apollographql.apollo.api.Query> Optional<Data> execute(
T operation) {
ApolloClient client = new CommonClient().getClient();
ApolloCall<Data> apolloCall = client.query(operation);
return Rx2Apollo.from(apolloCall)
.map(value -> Optional.of(value.data()))
.onErrorReturn(o -> {
logger.error(o.getMessage());
return Optional.empty();
})
.blockingFirst();
Most helpful comment
Here's my solution with
projectreactor: