Hello,
The recommended usage for ApolloClient is to create just a single ApolloClient. Is there a way to do this and send an authorization header based on the logged in user?
All the examples I've seen seem to set the header once on the creation of ApolloClient and that's it (e.g.: https://github.com/apollographql/apollo-android/issues/497). We are using ApolloClient for a web app, so we need to set the Authorization on every call.
It's up to the client to setup custom OkHttp interceptor and then make sure this interceptor picks up the latest valid token.
that makes sense, this is what we have right now:
ApolloClient.builder()
.serverUrl("GRAPHQL_API_URL_HERE")
.okHttpClient(new OkHttpClient.Builder()
.addInterceptor(chain -> chain.proceed(chain.request().newBuilder().addHeader("Authorization", "Bearer BEARER_TOKEN_HERE").build()))
.build())
.build();
It looks like the ApolloClient can only use the header added at the time of creation, is that correct? So if we need to make multiple calls with different bearer tokens (or even if we wanted to add in a request id for each request), that would require different instances of ApolloClient?
@sav007, after looking through the code some more, I think the ideal solution for web apps is to be able to optionally pass in headers when calling ApolloClient::query() or ApolloClient::mutate().
Perhaps we can update ApolloClient::newCall() to make a call to RealApolloCall.<T>builder().requestHeaders(REQUEST_HEADERS_HERE). What are your thoughts?
Yeah, I think it makes sense to add such feature.
cc @martinbonnin @tasomaniac
Comparing with other frameworks, Retrofit allows sending additional headers with individual network calls. They do it via annotations.
I am not sure how it would work for Apollo. Are you suggesting to add overloaded functions of query and mutate to have an extra headers parameter?
@justtonytone You can keep a reference to your interceptor and change the header at runtime. You just have to make sure the method is thread safe as the interceptor will be called from several background threads. For an exemple, you could do something like:
class MyInterceptor : Interceptor {
// You can change authorization here
@get:Synchronized
@set:Synchronized
var authorization: String? = null
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(chain.request().newBuilder()
.addHeader("Authorization", authorization)
.build())
}
}
This is also useful to handle token refresh.
@tasomaniac yes, I am suggesting overloaded functions for query and mutate. Although I am not the most familiar with those codebase, so there could be better solutions to this problem.
I don't think overloaded function is a good solution because we would end up with more and more for each header if we open up that road.
If you think about more, authorization is a separate concern. There might be public servers where you don't even need authorization to talk to.
@martinbonnin's suggestion makes sense to me. That would also be my recommended solution
@martinbonnin, thanks for the workaround. I feel like that is a bandaid to the actual problem.
I feel like adding headers via the interceptors was created for setting a default header, and is problematic for setting unique headers. For example, ensuring thread safety means we are throttled to how many requests we can make and will not be as performant. Whereas adding headers to each individual request is supported through OkHttpClient without these limitations.
@tasomaniac, I'm not sure I understand your concern. I'm envisioning the overloaded functions would just take RequestHeaders as a parameter (which contains a Collection of headers). So I don't see the need for more overloaded functions in the future.
If you think about more, authorization is a separate concern. There might be public servers where you don't even need authorization to talk to.
Agreed on that last point. This wouldn't be something that everyone uses and this case is probably not as important for android apps, but for web apps that are looking to make a lot of requests, I would prefer the solution that's more performant.
I don't think it is a workaround. Interceptor is meant to be dynamic. If it were for adding constant/static headers, then the API would simply be addHeader/setHeaders.
The official OkHttp documentation and recipes also recommend using Interceptor approach.
@tasomaniac I would say it's a workaround for adding headers that are meant to be unique for every request because:
Request request = new Request.Builder().url(URL).addHeader(HEADER_NAME, HEADER_VALUE).build();Operation represents a Request. Maybe this should be possible at Operation level. That way, no need to have overloaded functions.
That sounds good, are you thinking that something could be set when calling the query builder?
Looks like this is already possible. You can do
apolloClient.query(query)
.requestHeaders(requestHeadersWithAuth)
.enqueue(...);
Perfect! Thanks for catching that @tasomaniac, apologies for not seeing that earlier.
Here's the thread which created that functionality: https://github.com/apollographql/apollo-android/issues/1183
No worries :) This should go into documentation.
If we need to get the ResponseHeaders and retrieve such values inside the POJO classes generated by the https://github.com/apollographql/apollo-android/tree/master/apollo-gradle-plugin how can we do that? In my use case, I have an apollo graphQL API that given a particular request header is going to return a response in the HTTP headers. I really like to keep my code to be auto-generated by the schema and .graphql queries files
Most helpful comment
Looks like this is already possible. You can do