Repository: Khan/genqlient
Pull request #356 (2024-09-28 15:29:25)

Use generics for subscriptions responses

HaraldNordgren: @benjaminjkraft Thanks a lot for your feedback! I have thought about this a lot, and I think you are absolutely right. Introducing generics on the client layer like I did, would worsen the user experience. So it's not worth doing.

I will reduce the scope of this PR and still see if I find any useful refactor 🤗

Patch:
diff --git a/docs/subscriptions.md b/docs/subscriptions.md
index ee4d3d8..146e940 100644
--- a/docs/subscriptions.md
+++ b/docs/subscriptions.md
@@ -1,6 +1,6 @@
 # Using genqlient with GraphQL subscriptions
 
-This document describes how to use genqlient to make GraphQL subscriptions. It assumes you already have the basic [client](./client_config.md) set up. Subscription support is fairly new; please report any bugs or missing features! 
+This document describes how to use genqlient to make GraphQL subscriptions. It assumes you already have the basic [client](./client_config.md) set up. Subscription support is fairly new; please report any bugs or missing features!
 
 ## Client setup
 
diff --git a/generate/operation.go.tmpl b/generate/operation.go.tmpl
index 1b70890..4fe0113 100644
--- a/generate/operation.go.tmpl
+++ b/generate/operation.go.tmpl
@@ -48,20 +48,16 @@ func {{.Name}}(
         resp_,
     )
     {{end}}
-	
+
     return {{if eq .Type "subscription"}}dataChan_, subscriptionID_,{{else}}data_, {{if .Config.Extensions -}}resp_.Extensions,{{end -}}{{end}} err_
 }
 
 {{if eq .Type "subscription"}}
-type {{.Name}}WsResponse struct {
-	Data       *{{.ResponseName}}     `json:"data"`
-	Extensions map[string]interface{} `json:"extensions,omitempty"`
-	Errors     error                  `json:"errors"`
-}
+type {{.Name}}WsResponse graphql.BaseResponse[*{{.ResponseName}}]
 
 func {{.Name}}ForwardData(interfaceChan interface{}, jsonRawMsg json.RawMessage) error {
     var gqlResp graphql.Response
-	var wsResp {{.Name}}WsResponse
+    var wsResp {{.Name}}WsResponse
     err := json.Unmarshal(jsonRawMsg, &gqlResp)
     if err != nil {
         return err
diff --git a/graphql/client.go b/graphql/client.go
index 526395b..ac369d1 100644
--- a/graphql/client.go
+++ b/graphql/client.go
@@ -197,6 +197,12 @@ type Request struct {
 	OpName string `json:"operationName"`
 }
 
+type BaseResponse[T any] struct {
+	Data       T                      `json:"data"`
+	Extensions map[string]interface{} `json:"extensions,omitempty"`
+	Errors     gqlerror.List          `json:"errors,omitempty"`
+}
+
 // Response that contains data returned by the GraphQL API.
 //
 // Typically, GraphQL APIs will return a JSON payload of the form
@@ -206,11 +212,7 @@ type Request struct {
 // It may additionally contain a key named "extensions", that
 // might hold GraphQL protocol extensions. Extensions and Errors
 // are optional, depending on the values returned by the server.
-type Response struct {
-	Data       interface{}            `json:"data"`
-	Extensions map[string]interface{} `json:"extensions,omitempty"`
-	Errors     gqlerror.List          `json:"errors,omitempty"`
-}
+type Response BaseResponse[any]
 
 func (c *client) MakeRequest(ctx context.Context, req *Request, resp *Response) error {
 	var httpReq *http.Request
diff --git a/internal/integration/generated.go b/internal/integration/generated.go
index 8a8f70c..9bb972d 100644
--- a/internal/integration/generated.go
+++ b/internal/integration/generated.go
@@ -3119,11 +3119,7 @@ func count(
 	return dataChan_, subscriptionID_, err_
 }
 
-type countWsResponse struct {
-	Data       *countResponse         `json:"data"`
-	Extensions map[string]interface{} `json:"extensions,omitempty"`
-	Errors     error                  `json:"errors"`
-}
+type countWsResponse graphql.BaseResponse[*countResponse]
 
 func countForwardData(interfaceChan interface{}, jsonRawMsg json.RawMessage) error {
 	var gqlResp graphql.Response


Test patch:
diff --git a/generate/testdata/snapshots/TestGenerate-SimpleSubscription.graphql-SimpleSubscription.graphql.go b/generate/testdata/snapshots/TestGenerate-SimpleSubscription.graphql-SimpleSubscription.graphql.go
index 4f1ebca..158aa87 100644
--- a/generate/testdata/snapshots/TestGenerate-SimpleSubscription.graphql-SimpleSubscription.graphql.go
+++ b/generate/testdata/snapshots/TestGenerate-SimpleSubscription.graphql-SimpleSubscription.graphql.go
@@ -39,11 +39,7 @@ func SimpleSubscription(
 	return dataChan_, subscriptionID_, err_
 }
 
-type SimpleSubscriptionWsResponse struct {
-	Data       *SimpleSubscriptionResponse `json:"data"`
-	Extensions map[string]interface{}      `json:"extensions,omitempty"`
-	Errors     error                       `json:"errors"`
-}
+type SimpleSubscriptionWsResponse graphql.BaseResponse[*SimpleSubscriptionResponse]
 
 func SimpleSubscriptionForwardData(interfaceChan interface{}, jsonRawMsg json.RawMessage) error {
 	var gqlResp graphql.Response