hasura / go-graphql-client

Package graphql provides a GraphQL client implementation.
MIT License
405 stars 94 forks source link

add mechanism to modify outgoing http request #23

Closed dbarrosop closed 2 years ago

dbarrosop commented 2 years ago

The purpose of this PR is to allow users of this library to modify the outgoing request. It is useful as it allows you to set authentication headers without having to write a custom http client (ref. https://github.com/shurcooL/graphql/issues/28#issuecomment-464713908)

The mechanism implemented for this has two purposes:

  1. It maintains backwards compatibility
  2. It allows reusing the same client amongst slightly different "configurations". This allows you to have one single http client in multitenant applications with different authentication headers.

You can see a silly proof of concept below to illustrate how different requests can tweak the header slightly different while reusing the same TCP socket for performance reasons:

package main

import (
    "context"
    "fmt"
    "net/http"
    "sync"

    "github.com/hasura/go-graphql-client"
)

func setAuthHeader(secret string) func(req *http.Request) {
    return func(req *http.Request) {
        req.Header.Add("x-hasura-admin-secret", secret)
    }
}

func main() {
    client := graphql.NewClient("http://localhost:8080/v1/graphql", nil)

    wg := &sync.WaitGroup{}
    wg.Add(100)

    for i := 0; i < 100; i++ {
        go func(i int) {
            defer wg.Done()

            if i%2 == 0 {
                client = client.WithRequestModifier(setAuthHeader("hello123"))
            } else {
                client = client.WithRequestModifier(setAuthHeader("hello124"))
            }

            var query struct {
                StorageBucketsByPK struct {
                    ID graphql.String `graphql:"id"`
                } `graphql:"storage_buckets_by_pk(id: $id)"`
            }

            variables := map[string]interface{}{
                "id": graphql.String("default"),
            }

            err := client.Query(context.Background(), &query, variables)
            if err != nil {
                fmt.Println(err.Error())
            }
            fmt.Println(query.StorageBucketsByPK.ID)
        }(i)
    }

    wg.Wait()
}
advdv commented 2 years ago

It would be nice to have this on the *SubscriptionClient as well. That way we can easily add authorization header on WebSocket connections as well.