javaspringspring-bootspring-graphql

Accessing response headers in SyncGraphQlClientInterceptor


This question was originally asked on github issues. Posted here in hopes of getting an answer.

Please tell me is it possible to somehow get information about response headers in SyncGraphQlClientInterceptor? The problem we are trying to solve: There is a certain graphql server provider that returns apollo-trace-id in the response headers. On our side, we need to log this traceID to improve observability. We can get the response header at the ClientHttpRequestInterceptor level, but we want to log this identifier in two cases:

  1. If the provider's server returned an incorrect HTTP status.
  2. If the provider’s server returned incorrect data in the response (for example, there are some errors in the errors block of the response that do not allow this response to be considered valid).

And if the first point can be solved quite simply using ClientHttpRequestInterceptor, then the second point can be more difficult to solve using it. The problem is that ClientHttpRequestInterceptor does not have a deserialized response body and there is no easy way to parse it. And if we take SyncGraphQlClientInterceptor, then there is no access to response headers due to the lack of such data in GraphQlResponse and the transfer of this information to HttpSyncGraphQlTransport. There are essentially two options:

  1. Create a certain context for storing apollo-trace-id at the ClientHttpRequestInterceptor level (a class with the ThreadLocal field) and then pick it up at the SyncGraphQlClientInterceptor level.
  2. Additionally deserialize the response body at the ClientHttpRequestInterceptor level and log information about traceID there.

But both options, to be honest, look so-so. Maybe there are some better options that we haven't noticed?


Solution

  • I think your first solution looks like the most straightforward solution for a synchronous client (this would not work with the reactive variant).

    GraphQL interceptors, by design, do not expose information about the underlying transport because GraphQL is transport agnostic. On the client side, we relaxed this a bit with the ClientGraphQlRequest attributes map that are being copied to the HTTP request attributes map on the way out. This could be a way for you to set an attribute there in the SyncGraphQlClientInterceptor to later set information on it in the ClientHttpRequestInterceptor.

    In the case where you get an invalid HTTP response status, I'm wondering if you shouldn't get a WebClientResponseException in the first place, which contains all the information you need (including response headers).

    Now there might be ways to improve the situation here, and those could be considered as enhancement requests if they fit your use case:

    1. implement observability support in the GraphQL clients. Right now this is only supported at the HTTP client level. This should give metrics, traces support and more for GraphQL client requests. This would natively work with brave/OTLP headers, but I'm not sure if and how Micrometer supports apollo tracing headers. Maybe the apollo server can send such standard headers as well?
    2. Consider an attributes map variant on the response that would allow client interceptors to add more metadata to the client response. So, an equivalent of the ClientGraphQlRequest with an attributes map, but for the response.