Skip to content

Releases: hasura/go-graphql-client

v0.9.2

17 Mar 15:20
0acd9d9
Compare
Choose a tag to compare

This release adds new callback events for the subscription client that are useful for debugging and testing.

// OnConnectionAlive event is triggered whenever the WebSocket connection 
// receives a connection alive message (differs per protocol)
client.OnConnectionAlive(fn func())

// OnSubscriptionComplete event is triggered when the subscription receives 
// a terminated message from the server
client.OnSubscriptionComplete(fn func(sub Subscription))

New settings are also added to control the retry and exit behaviors of the subscription client.

client.WithoutLogTypes(graphql.GQLData, graphql.GQLConnectionKeepAlive).
    // the client should exit when all subscriptions were closed, default true
    WithExitWhenNoSubscription(false).
    // WithRetryStatusCodes allow retrying the subscription connection when receiving one of these codes
    // the input parameter can be a number string or range, e.g 4000-5000 
   WithRetryStatusCodes("4000", "4000-4050")

Changelog

  • add OnConnectionAlive event to the subscription client (#77) @sermojohn
  • fix: prevent panic on late message after unsubscribed subscription (#79) @sermojohn
  • improve the subscription life cycle events and add new settings (#82) @hgiasac

v0.9.1

28 Feb 17:09
bb54675
Compare
Choose a tag to compare

Changelog

  • improve the subscription client, fix goroutine leaks and data race issues (#76) @hgiasac
  • merge the reset subscription logic into the Run method. If you call the Run method when the subscription client is running, the client will restart the current connection.
  • print the extra Extensions information in the Error object

v0.9.0

13 Feb 07:54
10471cf
Compare
Choose a tag to compare

Highlight

Support graphql-ws protocol

The subscription client now supports 2 protocols:

The protocol can be switchable by the WithProtocol function. By default, the subscription client uses the subscriptions-transport-ws protocol.

client := graphql.NewSubscriptionClient("wss://example.com/graphql").
    WithProtocol(graphql.GraphQLWS)

Changelog

  • exposed the UnmarshalGraphQL function in the jsonutil package (#62) @nico151999
  • fix the dynamic GraphQL type output from the GraphQLType interface instance (#56) @hgiasac
  • support graphql-ws protocol (#67) @hgiasac
  • allow using custom HTTP client that implements Doer interface (#68) @senekis
  • patch gin v1.7.7 to fix the security issue (#75) @hgiasac

v0.8.1

29 Sep 07:58
835e565
Compare
Choose a tag to compare

v0.8.0

09 Aug 05:44
93707b1
Compare
Choose a tag to compare

Highlight

Introduce ExecRaw method that returns a raw json message.

query := `query{something(where: { foo: { _eq: "bar" }}){id}}`
var res struct {
	Somethings []Something `json:"something"`
}

raw, err := client.ExecRaw(ctx, query, map[string]any{}) 
if err != nil {
	panic(err)
}

err = json.Unmarshal(raw, &res)

Breaking change: currently QueryRaw, MutateRaw and Subscribe methods return *json.RawMessage. This output type is redundant to be decoded. The output type should be changed to []byte.

var subscription struct {
	Me struct {
		Name graphql.String
	}
}

subscriptionId, err := client.Subscribe(&query, nil, func(dataValue []byte, errValue error) error {
	if errValue != nil {
		// handle error
		// if returns error, it will failback to `onError` event
		return nil
	}
	data := query{}
	err := json.Unmarshal(dataValue, &data)

	fmt.Println(query.Me.Name)

	// Output: Luke Skywalker
	return nil
})

if err != nil {
	// Handle error.
}

Changelog

  • Propagate errors instead of panic when building the GraphQL string by reflection (#41) @grihabor
  • add ExecRaw and change the output type of Raw methods. Remove canonical/vanity import path (#44) @hgiasac

v0.7.2

05 Jul 06:34
7e9cc29
Compare
Choose a tag to compare

This version focuses on subscription fixes and improvements (#39)

  • Fix the deadlock issue that the subscription client can't receive error events from the channel.
  • Close the connection when there is no running subscription
  • add a particular error that stops the subscription inside the callback
subscriptionId, err := client.Subscribe(&query, nil, func(dataValue *json.RawMessage, errValue error) error {
	// ...
	// return this error to stop the subscription in the callback
	return ErrSubscriptionStopped
})

v0.7.1

10 Jun 09:31
391115f
Compare
Choose a tag to compare
  • Rename SubscribeRaw to Exec in the subscription client to make it consistent with the HTTP client (#35)

v0.7.0

06 May 18:58
d4051ab
Compare
Choose a tag to compare

Changelog

  • Introduce Exec method to execute pre-built query string (#32) @dbarrosop
  • Allow specifying GraphQL type name by implementing GetGraphQLType method (#33) @nizar-m

Execute

The Exec function allows you to executing pre-built queries. While using reflection to build queries is convenient as you get some resemblance of type safety, it gets very cumbersome when you need to create queries semi-dynamically. For instance, imagine you are building a CLI tool to query data from a graphql endpoint and you want users to be able to narrow down the query by passing cli flags or something.

// filters would be built dynamically somehow from the command line flags
filters := []string{
   `fieldA: {subfieldA: {_eq: "a"}}`,
   `fieldB: {_eq: "b"}`,
   ...
}
query := "query{something(where: {" + strings.Join(filters, ", ") + "}){id}}"
res := struct {
	Somethings []Something
}{}
if err := client.Exec(ctx, query, &res, map[string]any{}); err != nil {
	panic(err)
}

Specify GraphQL type name

The GraphQL type is automatically inferred from Go type by reflection. However, it's cumbersome in some use cases, e.g lowercase names. In Go, a type name with a first lowercase letter is considered private. If we need to reuse it for other packages, there are 2 approaches: type alias or implement GetGraphQLType method.

type UserReviewInput struct {
	Review String
	UserID String
}
// type alias
type user_review_input UserReviewInput
// or implement GetGraphQLType method
func (u UserReviewInput) GetGraphQLType() string { return "user_review_input" }
variables := map[string]interface{}{
  "input": UserReviewInput{}
}
//query arguments without GetGraphQLType() defined
//($input: UserReviewInput!)
//query arguments with GetGraphQLType() defined:w
//($input: user_review_input!)

v0.6.5

15 Mar 07:31
281df85
Compare
Choose a tag to compare

Skip GraphQL field with hyphen tag. Inspired from encoding/json library (#31) @hgiasac

struct {
    Viewer struct {
	ID         interface{} `graphql:"-"`
	Login      string
	CreatedAt  time.Time `graphql:"-"`
	DatabaseID int
   }
}

// Output
// {viewer{login,databaseId}}

v0.6.4

14 Mar 09:34
7e27ec6
Compare
Choose a tag to compare

Changelog

  • support custom HTTP client for the subscription client (#29) @sermojohn
  • add debug mode, expose GraphQL builder functions and UnmarshalGraphQL for unit tests (#30) @hgiasac

Custom HTTP Client for the subscription client

Use WithWebSocketOptions to customize the HTTP client which is used by the subscription client.

client.WithWebSocketOptions(WebsocketOptions{
	HTTPClient: &http.Client{
		Transport: http.DefaultTransport,
		Timeout: time.Minute,
	}
})

Debugging and Unit test

Enable debug mode with the WithDebug function. If the request is failed, the request and response information will be included in extensions[].internal property.

{
	"errors": [
		{
			"message":"Field 'user' is missing required arguments: login",
			"extensions": {
				"internal": {
					"request": {
						"body":"{\"query\":\"{user{name}}\"}",
						"headers": {
							"Content-Type": ["application/json"]
						}
					},
					"response": {
						"body":"{\"errors\": [{\"message\": \"Field 'user' is missing required arguments: login\",\"locations\": [{\"line\": 7,\"column\": 3}]}]}",
						"headers": {
							"Content-Type": ["application/json"]
						}
					}
				}
			},
			"locations": [
				{
					"line":7,
					"column":3
				}
			]
		}
	]
}

Because the GraphQL query string is generated in runtime using reflection, it isn't really safe. To assure the GraphQL query is expected, it's necessary to write some unit test for query construction.

// ConstructQuery build GraphQL query string from struct and variables
func ConstructQuery(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

// ConstructQuery build GraphQL mutation string from struct and variables
func ConstructMutation(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

// ConstructSubscription build GraphQL subscription string from struct and variables
func ConstructSubscription(v interface{}, variables map[string]interface{}, options ...Option) (string, error) 

// UnmarshalGraphQL parses the JSON-encoded GraphQL response data and stores
// the result in the GraphQL query data structure pointed to by v.
func UnmarshalGraphQL(data []byte, v interface{}) error