Apollo-android: Kotlin Native Apollo client / runtime

Created on 26 Apr 2020  路  4Comments  路  Source: apollographql/apollo-android

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:

  • our existing runtime is so deeply coupled with OkHttp network library that limits us on adding new transport layers such as web socket, mocked network client. Community were asking us to add such features but with current arch it's almost impossible. On top of that with KN there is no multi-platform implementation of OkHttp.
  • having public API in ApolloClient related to network / normalized cache, always raised concerns from large companies (like Shopify, Netflix etc.) regarding been too opinionated, the feedback we got Apollo is like all or nothing in solution. Sometimes such companies have own network stack, infra to perform GraphQL operations. We made a huge step towards making our models been used without runtime but we need to redesign our runtime API and make cache layer is truly optional even from API perspective.

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:

  • NetworkRequestExecutor - should be the last executor in the chain as it performs network execution via provided network transport layer
  • CacheRequestExecutor - normalized cache
  • AutoPersistedQueryRequestExecutor - to support auto persisted queries
  • SubscriptionRequestExecutor - GraphQL subscription support
  • BatchedRequestExecutor - nice to have, responsible query batching
Runtime Discussion Feature

Most helpful comment

Just a question: What is KN?

Kotlin Native

All 4 comments

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?

That's the idea, yes.

Just a question: What is KN?

Just a question: What is KN?

Kotlin Native

Was this page helpful?
0 / 5 - 0 ratings