smallrye-graphql icon indicating copy to clipboard operation
smallrye-graphql copied to clipboard

Add a way to dynamically provide headers for clients

Open jmartisk opened this issue 3 years ago • 1 comments

.. like maybe an SPI that allows to plug in a HeaderProvider that gets called before every request to provide the HTTP headers...?

Typesafe client can already get headers dynamically by using @Header(method="headerProviderMethod"), but this new feature could be supported for both client types.

jmartisk avatar Apr 22 '22 08:04 jmartisk

There was already something like this in one of the first versions of the typesafe client. We removed it, as it can be difficult to understand where the header comes from.

t1 avatar Apr 27 '22 05:04 t1

True that it will be a bit confusing, but how else can we support dynamic header values...

Potentially client builders could accept a Function<SomeContext, String> that is called always when a header value is needed, SomeContext would be some kind of context that might be necessary to compute the value, but I'm not sure what exactly that context should contain (and maybe we don't need that context at all).

jmartisk avatar Nov 10 '22 09:11 jmartisk

Hmmm... is there really a use-case to dynamically create header names? For dynamic values you can use the @Header(name = "Foo", method="myFooMethod") syntax.

t1 avatar Nov 10 '22 16:11 t1

No I'm not suggesting dynamic header names. The Function<SomeContext, String> would be assigned to a single header with a particular name, and only compute its value over time (that's the resulting String).

jmartisk avatar Nov 11 '22 08:11 jmartisk

If you don't want to repeat the @Header(name = "foo", method = "MyClass.myMethod") on multiple APIs, you can extract this into a Stereotype. I think this is much cleaner that having some headers magically appear without any visible clue.

t1 avatar Nov 11 '22 11:11 t1

Yeah, but in this issue I'm focusing on how to enable this with dynamic clients (they don't have it at all), and these don't have any reasonable place where to put an annotation, that's why I'm suggesting something that can be passed to the client builder programmatically.

jmartisk avatar Nov 11 '22 12:11 jmartisk

I needed custom per function headers (dynamically) with the dynamic client, so I had to roll out a custom client based on vertx as well to do it. Too bad smallrye client doesn't fit my needs

riccardocossu avatar Mar 23 '23 15:03 riccardocossu

@riccardocossu, Does that mean that you need a different header depending on what operation is being invoked? We could quite easily add that - I'm mostly thinking what kind of context is necessary to pass to the header supplier, so possibly a JsonObject representing the whole GraphQL request would be the answer.

In which case, I would imagine adding this method to both TypesafeGraphQLClientBuilder and DynamicGraphQLClientBuilder:

header(String headerName, Function<JsonObject, String> headerValueSupplyingFunction);

Would that resolve your missing functionality?

jmartisk avatar Mar 24 '23 06:03 jmartisk

@jmartisk thanks for your reply; I am using the client as a singleton, I plan to have just one of them around, or maybe a few. Certainly not one per request, so the builder doesn't seem to be a viable option.

What I need is to send custom headers whose name and value are dependent on what I get as input parameters; this may potentially vary on every request. What I did it's actually creating a new function:

public Uni<Response> executeAsync(String query, Map<String,Object> variables, Map<String, String> headers)

so that the caller decides which headers will be in the request, with complete control over them. I only need that for now, async execution of a query as string. That would a viable solution for me (I understand that it would have to be added to all the methods in that interface).

riccardocossu avatar Mar 24 '23 07:03 riccardocossu

Certainly not one per request, so the builder doesn't seem to be a viable option.

Using the builder is not "one per request", you build a GraphQL client with the builder and then you can reuse it for many requests. Only the Function<JsonObject, String> would be called for each request.

But still, I see your point and I yeah I think adding variants of executeSync, executeAsync and subscription methods that take a headers parameter is probably more straightforward and more useful. These passed dynamic headers would be merged with headers passed via the builder (or static configuration). I'll look into implementing this.

jmartisk avatar Mar 24 '23 08:03 jmartisk

Ok I see your point; both will works as long as every request is able to send whatever headers they please. Thanks for your help

riccardocossu avatar Mar 24 '23 08:03 riccardocossu