Khan / genqlient

a truly type-safe Go GraphQL client
MIT License
1.07k stars 107 forks source link

Is there a way to access the raw http response? #293

Closed adrianocr closed 1 year ago

adrianocr commented 1 year ago

Is your feature request related to a problem? Please describe. I am using genqlient to query the shopify graphql api. It works fine but the shopify api is rate limited to 50 data points per second. To facilitate not being throttled the shopify api includes rate limit information (leaky bucket algorithm) as part of the response. A response looks like this:

{
    "data": {
        "products": {
            "nodes": []
        }
    },
    "extensions": {
        "cost": {
            "requestedQueryCost": 4,
            "actualQueryCost": 3,
            "throttleStatus": {
                "maximumAvailable": 1000.0,
                "currentlyAvailable": 997,
                "restoreRate": 50.0
            }
        }
    }
}

The extensions object provides the rate limit data. But genqlient only makes the products part of the response available.

Describe the solution you'd like Ideally it would be nice to access extra data as part of the type genqlient returns for each query, but if that's not possible then accessing the raw http response would allow for a user to manually marshal the data into a type and access it.

benjaminjkraft commented 1 year ago

Two options here that exist today:

First, customize the graphql.Client -- basically, write your own implementation of the interface (likely extending the provided one similar to how auth works) that extracts those extensions. (Actually, we already parse them to a map[string]interface{}.) That works well if you want to, say, retry at a given time, or attach data to the error, or update the context somehow (say, if you want to keep track in some quasi-global way of the last rate limit status). But it doesn't really let you add a field to the response for the caller to handle.

Alternately, the option use_extensions adds a return parameter with the extensions. This was added in #184 (see the issue #183 for a bit of discussion on why it's set up this way). It will work better if you want the caller to handle the extensions.

While writing this I realize we could maybe do better on the second half by letting you make the extensions typed? Something like

# genqlient.yaml
extensions_type: path/to/mypkg.Extensions
package mypkg
type Extensions struct {
  Cost struct { ... } `json:"cost"
}

and then instead of map[string]interface{} you get *mypkg.Extensions. Arguably we could even do that if you don't have use_extensions, just for the benefit of a custom graphql.Client. Not sure how important that is for your use case, but it seems reasonable to add.

adrianocr commented 1 year ago

use_extensions is perfect, thank you so much. I had no idea it was an option. Resolved my issues