apollographql / apollo-kotlin

:rocket:  A strongly-typed, caching GraphQL client for the JVM, Android, and Kotlin multiplatform.
https://www.apollographql.com/docs/kotlin
MIT License
3.76k stars 653 forks source link

[umbrella issue] Java Runtime #3694

Closed martinbonnin closed 1 year ago

martinbonnin commented 2 years ago

Now that apollo-runtime brings kotlinx.coroutines as a dependency, it's not as easy to work with interceptors, etc.. in Java because everything is a Flow or suspend function.

This issue is to gather interest about writing an alternative runtime that can reuse a lot of the common parts (apollo-api, normalization, cache, etc...) but expose a coroutine-free runtime.

kicktipp commented 2 years ago

I would love to see better java support. At least it should be documented how to use the lib with java. I tried yesterday to upgrade to 3.0 but failed.

MatthewFrench commented 2 years ago

Same, I'm seeing problems using apollo Optional.Absent and graphql query errors like Type parameter '...Data' is not within its bound; should implement 'com.apollographql.apollo3.api.Operation.Data'.

martinbonnin commented 2 years ago

Hi @MatthewFrench ! Do you have sample code/a reproducer? Maybe we can fix this one without implementing a separate coroutine-free Java runtime.

cristian1980 commented 2 years ago

Hello, I quite advanced in a project in Java and I am trying to use 3.0. Everything is ok until, try to run a query. I couldn't find a single example on how run a query from java, there is this: https://www.apollographql.com/docs/kotlin/advanced/java But examples stop before a query code is demonstrated.

I am pretty new to kotlin so I am a bit lost. Bellow it's image with the things I tried and I am blocked by a similar error as @MatthewFrench All 3 red squiggly lines are this error: Type parameter '...Data' is not within its bound; should implement 'com.apollographql.apollo3.api.Operation.Data'.

Screen Shot 2022-04-01 at 01 17 42
BoD commented 2 years ago

Hi @cristian1980 👋

Currently the recommended way to use Apollo Kotlin from Java is through the RxJava adapters, which are described here. Basically you would do something like:

Single<ApolloResponse<GetAppConfigQuery.Data>> queryResponse = Rx2Apollo.single(
        apolloClient.query(new GetAppConfigQuery())
);
queryResponse.subscribe( 
        response -> {
          // Use response
        },
        throwable -> {
          // Handle error
        }
);
brendanlong commented 2 years ago

I'm using Rx3Apollo and it works for me in gradle but I get the same error in Intellij:

Screen Shot 2022-05-05 at 6 51 15 PM Screen Shot 2022-05-05 at 6 51 28 PM

Weirdly enough, it works fine when I build on the command line with Gradle, so it's possible something weird is going on in Intellij?

If I manually edit my generated file and change:

public static class Data implements Mutation.Data {

to:

public static class Data implements Mutation.Data, Operation.Data {

... everything works, but I don't know why that would matter since Mutation.Data supposedly already extends Operation.Data.

Screen Shot 2022-05-05 at 6 56 10 PM

I almost never use Java so it's possible I'm missing something obvious here, but wanted to document it in case other people are having this problem too.

BoD commented 2 years ago

Hi @brendanlong 👋

I don't seem to reproduce the issue on a simple project (with IntelliJ IDEA 2022.1). But seeing that it works on the command line, this would indeed seem to be some kind of issue with IntelliJ.

kkunan commented 2 years ago

One possible workaround for people who really need to use Java here, is to create a wrapper class. It requires mixing with Kotlin a little bit.

  1. Create ApolloWrapper.kt, similar to this

    
    fun <D: Query.Data> query(client: ApolloClient,
                          query: Query<D>,
                          listener: QueryApolloResponseListener<D>){
    CoroutineScope(Dispatchers.IO).launch {
        val response = client.query(query).execute()
        response.data?.let {
            listener.onSuccess(it)
        } ?: kotlin.run {
            listener.onFailure(response.errors ?: emptyList())
        }
    
    }
    }

fun mutate(client: ApolloClient, mutation: Mutation, listener: MutationApolloResponseListener){ CoroutineScope(Dispatchers.IO).launch { val response = client.mutation(mutation).execute() response.data?.let { listener.onSuccess(it) } ?: kotlin.run { listener.onFailure(response.errors ?: emptyList()) }

}

}

interface QueryApolloResponseListener { fun onSuccess(data: T) fun onFailure(error: List) }

interface MutationApolloResponseListener { fun onSuccess(data: T) fun onFailure(error: List) }


2. In your java file, do something like this

public void call(){ ApolloClient client = new ApolloClient.Builder() .serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql") .build(); LoginMutation mutation = new LoginMutation("testEmail"); ApolloWrapperKt.mutate(client, mutation, new ResponseListener()); }

static class ResponseListener implements MutationApolloResponseListener<LoginMutation.Data> {
    @Override
    public void onSuccess(@NonNull LoginMutation.Data data) {
        System.out.println(data);
    }

    @Override
    public void onFailure(@NonNull List<Error> error) {
        System.err.println(error);
    }
}


So far the compiler haven't complained and I got the response back correctly.
stengvac commented 2 years ago

Type parameter '...Data' is not within its bound; should implement 'com.apollographql.apollo3.api.Operation.Data'

This probably is not Apollo problem. I have tried some combinations. Kotlin generated classes work fine in Java classes in same gradle module, it seems this is broken when those are made as dependencies and only for IDE - project still compile and is runnable so it is Idea bug?

But in the end yes write generic wrapper public <T extends Mutation.Data> T mutate(Mutation<T> request) { } for those generated classes in java is problematic. Method definition is fine, problem is when called. Then reason: no instance(s) of type variable(s) exist so that Data conforms to Data happens.

stengvac commented 2 years ago

Reported bug to Idea: https://youtrack.jetbrains.com/issue/IDEA-296915

jpvajda commented 2 years ago

Hello everyone! Thanks for raising this concern on our support of Java. We appreciate the feedback. 🙏

We are trying to better understand the use of Java with our Kotlin Client, and wanted to ask :

Are any of you using Java for Mobile Android development, or is this something else all together? Any additional information would help us tremendously to better understand your use cases here. Thank you!

kicktipp commented 2 years ago

We are not using for Android mobile. We are using it as a Java client for a graphql backend. We never used kotlin before.

stengvac commented 2 years ago

Hello,

We are using it as client between BE services. Some of them are written in Java and https://youtrack.jetbrains.com/issue/IDEA-296915 make it hard to use generated Kotlin client. Once this issue is gone (should be with Kotlin compiler 1.8) then we can swap to Kotlin.

jpvajda commented 2 years ago

@kicktipp and @stengvac thank you for responding!

maclockard commented 1 year ago

I have nearly the same use case as @stengvac. IDE support, lack of documentation, and lack of an easy to use java runtime is making the migration to 3.x quite tricky.

bignimbus commented 1 year ago

We think that our experimental Java support is feature-complete for the 4.0.0 release, the remaining tasks have not yet been prioritized. We'll keep them open but in the interest of keeping our task list clean we'll close this umbrella Issue.