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

RFC: Handling lazy evaluation/passing in secrets, e.g. for hasura

Open bf4 opened this issue 3 years ago • 1 comments

A hasura-generated endpoint I work with is authenticated and requires a header "x-api-key" => api_key

I have a client class which takes in a client_name and looks up some configuration information, such as an api_key, which is not available globally.

the graphql-client gem would recommend I define

HTTP  = ::GraphQL::Client::HTTP.new(uri) do
              def headers(_context)
                # Optionally set any HTTP headers
                {
                  "Accept" => "application/json;charset=UTF-8",
                  "Content-Type" => "application/json",
                  "x-api-key" => api_key,
                }
              end

but I don't have access to that 'context' yet, so I have an instance method like

@http  ||= 
  begin
    instance = self
    ::GraphQL::Client::HTTP.new(uri) do
              define_method :headers do |_context|
                # Optionally set any HTTP headers
                {
                  "Accept" => "application/json;charset=UTF-8",
                  "Content-Type" => "application/json",
                  "x-api-key" => instance.api_key,
                }
              end
  end

I know that code pattern makes me feel like I'm fighting the library, but I persist because I can't @schema ||= GraphQL::Client.load_schema(http) and @client ||= ::GraphQL::Client.new(schema: schema, execute: http) without the authenticated 'http', which requires authentication.

Now, I know that when I query like client.query(definition, variables: variables, context: client_context) that the 'client_context' can include the auth keys, but I don't get access to that until after the 'schema' has been loaded by the 'http'. (Now, I can possibly get around that by loading the schema from a file, but that also feels like fighting the library).

But the problem really beings when I client.parse(definition) because there's a requirement that that the returned module have a 'name' (presumably duck-typing that it's been set to a constant). I can work around this by

        @client ||= ::GraphQL::Client.new(schema: schema, execute: http).tap do |client|
          client.allow_dynamic_queries = true
        end

but since allow_dynamic_queries is deprecated, I know that this is both fighting the library and not a long-term solution.

I've searched around in this repository and a few others and haven't found anything which addresses these issues.

Hopefully this makes sense.

The alternative would a factory pattern which builds the Http, Client, Schema, and all the Definitions.. but that seems like more meta-programming than I'm up for right now.

bf4 avatar May 18 '22 19:05 bf4

@josh this a you thing?

bf4 avatar Jun 03 '22 00:06 bf4