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

MegaIssue: Kotlin Native Apollo client / runtime #2222

Closed sav007 closed 2 years ago

sav007 commented 4 years ago

Is your feature request related to a problem? Please describe. Apollo client moving towards KN, there is a need of having KN runtime / client module that is responsible for GraphQL operation execution.

Describe the solution you'd like One possible and straightforward solution is to port existing Apollo runtime module to KN. Even if this looks like reasonable thing to do there are 2 major concerns regarding this:

As a staring point of conversation regarding the vision of new design for our runtime module here is the high level diagram:

ApolloClient

Network Transport Layer:

Responsible for network communication, establishing connection, sending and receiving GraphQL request / response

interface GraphQLNetworkTransport {
  fun <T> send(request: Request<T>): Flow<Response<T>>
}

class Request<T>(
  val operation: Operation<*, T, *>,
  val scalarTypeAdapters: ScalarTypeAdapters,
  val executionContext: ExecutionContext // similar idea as coroutine context
)

sealed class Response<T> {

  data class Success<T>(
    val operation: Operation<*, T, *>,
    val data: T?,
    val errors: List<Error>,
    val executionContext: ExecutionContext // similar idea as coroutine context
  ) : Response<T>()

  data class Failure<T>(
    val operation: Operation<*, T, *>,
    val error: ApolloError,
    val executionContext: ExecutionContext // similar idea as coroutine context
  ) : Response<T>() 
}

The reason why send returns Flow instead of being suspended is because of GraphQL subscriptions that can emit multiple responses.

Any implementation of GraphQLNetworkTransport can extend response via context and provide any additional info to the user (such as http response headers in case of Http transport layer, this feature we've been asked for). It works in the same way as coroutine context.

With such abstraction user can easily provide own implementation of transport layer if default Apollo implementations are not suited. By default Apollo is going to provide 2 types of transport layers: Http / Web socket. Both multi-platform targeting Android / iOS.

GraphQL Execution Layer:

Responsible for GraphQL operation execution, Represented as a chain of executors / interceptors. Each executor / interceptor has own responsibility.

interface RequestExecutor {

  fun <T> execute(
      request: Request<T>,
      executorChain: RequestExecutorChain
  ): Flow<Response<T>>

}

interface RequestExecutorChain {

  fun <T> proceed(request: Request<T> ): Flow<Response<T>>

}

Very similar to OkHttp interceptor concept, this abstraction provides flexibility in terms of implementation details and be able to chain different executors. Each executor in the chain can decide if it can short cut and return response right away or pass it down the chain and be able to decorate response.

User can provide own custom implantation of RequestExecutor and add it to the execution chain.

Out of the box Apollo is going to provide several request executors:

designatednerd commented 4 years ago

Am I correct in understanding that all cancellation mechanisms would be handled through the Flow API, and that's why there's no cancel on any of these?

sav007 commented 4 years ago

That's the idea, yes.

geckour commented 4 years ago

Just a question: What is KN?

martinbonnin commented 4 years ago

Just a question: What is KN?

Kotlin Native

martinbonnin commented 2 years ago

Looks like we're good to go 🎉 😃