Hi Everyone!
I have a graphQL API to query that requires HTTP request headers for each request and that returns the relative HTTP response headers
I found the response to half of the problem that I am trying to solve from https://github.com/apollographql/apollo-android/issues/2030#issuecomment-647873074
It looks like I can use the method provided by https://github.com/apollographql/apollo-android/blob/master/apollo-runtime/src/main/java/com/apollographql/apollo/internal/RealApolloCall.java#L178-L183 to add the request header for each request and now I was wondering how I can map the HTTP response headers to the POJO class that implements an Operation.Data. https://github.com/apollographql/apollo-android/blob/master/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operation.kt#L40
Maybe I am missing something and it is already there in some bean property?
Hi! You can get the HTTP response headers from response.executionContext:
val response: Response<YourQuery.Data> = //... get a response using ApolloClient
val httpContext = response.executionContext[OkHttpExecutionContext.KEY]
httpContext?.response?.headers?.forEach {
println("${it.first}=${it.second}")
}
This is a fairly new API and it will most likely require a recent version. Also, it's not documented yet but it should certainly be added somewhere. Let me know if it worked and I'll add it.

I am using apollo-api-jvm:2.2.1 Is it only in Kotlin such support?
public <T extends Query, V extends Operation.Data> Optional<V> execute(Query operation, Locale locale) {
ApolloCall<V> apolloCall = ApolloClient.builder()
.serverUrl(String.format(myProperties.getServerUrl(), locale.getCountry(), locale.toLanguageTag()))
.okHttpClient(okHttpClient)
.build()).query(operation).requestHeaders(
RequestHeaders.builder()
.addHeader("Authorization", "blablabla")
.build());
/* Code to understand how to get the response headers
ApolloClient apolloClient = ApolloClient.builder().build();
Response<Operation.Data> r = apolloClient.query(operation);
r.getExecutionContext().plus()
*/
Object objectResponse = Rx2Apollo.from(apolloCall)
.onErrorReturn(e -> {
Builder<V> builder = Response.builder(operation);
builder.errors(Lists.newArrayList(new Error(e.getMessage(), Collections.EMPTY_LIST, Collections.EMPTY_MAP)));
logger.error("It was not possible to process the request for {}", e.getMessage());
return builder.build();
})
.blockingFirst();
if (objectResponse instanceof Response) {
Response response = (Response) objectResponse;
if (response.hasErrors()) {
List<String> errors = (List<String>) response.getErrors().parallelStream()
.filter(i-> i instanceof Error)
.map(i-> ((Error) i).getMessage())
.collect(Collectors.toList());
return Optional.of((V) new OperationDataError(errors));
} else {
return Optional.of((V) response.getData());
}
}
logger.error("Not request processed for {}", operation.queryDocument());
return Optional.empty();
}
The interceptors are not thread-safe like the headers. If I add an interceptor to the apolloClient such interceptor is going to kick-in for all the requests dispatched by such apolloClient.
It would be also nice to embed the errors inside the Operation so I can avoid writing such horrible code :)
final OkHttpExecutionContext context = response.getExecutionContext().get(OkHttpExecutionContext.KEY);
Map<String,List<String>> responseHeaders = Optional.ofNullable(context)
.map(OkHttpExecutionContext::getResponse)
.map(okhttp3.Response::headers)
.map(Headers::toMultimap)
.orElse(Collections.emptyMap());
This is the solution to retrieve the headers. I upgraded the apollo-runtime to 2.2.1.
It looks like the ApolloClient hides some capabilities of the OkHttpClient and it looks to don't be trivial
avoid keeping a map of beans without dealing with OKHttpInterceptors when you need to change only requestParams in the URL
ApolloCall<V> apolloCall = ApolloClient.builder()
.serverUrl(String.format(myProperties.getServerUrl(), locale.getCountry(), locale.toLanguageTag()))
.okHttpClient(okHttpClient)
.build()).query(operation).requestHeaders(
RequestHeaders.builder()
.addHeader("Authorization", "blablabla")
.build());
Hi!
Glad to hear that retrieving the HTTP response headers is working 馃憤
Regarding OKHttpInterceptors, I'm not sure to understand what you are using them for. Can you elaborate a bit more? To change the headers on an ApolloCall, you can use ApolloCall.requestHeaders() without a OkHttpInterceptor
Yes, the headers problem is solved.
ApolloCall<V> apolloCall = ApolloClient.builder()
.serverUrl(String.format(myProperties.getServerUrl(), locale.getCountry(), locale.toLanguageTag()))
.okHttpClient(okHttpClient)
.build()).query(operation);
I am referring to this snippet as you can see I would need to change the serverUrl for each request to add the java.util.Locale depending on the user request.
myProperties.getServerUrl is something like https://mygreatgraphqlserver/blablabla?country=%s&language=%s and I wanted to avoid to generate an ApolloClient Bean for each request, or the ApolloClient bean is designed by taking in consideration that you can have a new one for each request?
Ah yes, there's no way to change the serverUrl per-request. What you can do is create a "generic" ApolloClient and use ApolloClient.newBuilder() to change the serverUrl without instantiating a new OkHttpClient every time:
// Initial generic client
ApolloClient genericClient = ApolloClient.builder()
.serverUrl("dummyUrl")
.okHttpClient(okHttpClient)
.build()
// When you want to change the serverUrl:
ApolloClient apolloClient = genericClient.newBuilder()
.serverUrl("https://your.real/url")
.build()
This way, the underlying OkHttpClient instance and underlying threadpool will be reused.