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

Issue with inferring type that implements GraphQLQuery

Open Ejhfast opened this issue 4 years ago • 6 comments

I am trying to make a generic function to send queries to a server:

fn make_graphql_request<G: GraphQLQuery>(url: &str, query: &QueryBody<G::Variables>) -> Response<G::ResponseData>{
    let client = reqwest::Client::new();
    let mut res = client
        .post(url)
        .json(&query)
        .send().unwrap();
    res.json().unwrap()
}

Then call it on a specific kind of QueryBody:

let q: QueryBody<list_markets_id::Variables> = ListMarketsId::build_query(list_markets_id::Variables);
let res_body = make_graphql_request(&"https://myurl/api/graphql", &q) as Response<list_markets_id::ResponseData>;

What happens is this:

30 |     let res_body = make_graphql_request(&"https:/myurl.io/api/graphql", &q) as Response<list_markets_id::ResponseData>;
   |                    ^^^^^^^^^^^^^^^^^^^^ cannot infer type for `G`

What is the type that implements list_markets_id::Variables and list_markets_id::ResponseDara? I am new to Rust so I'm sure there must be a simple solution I am missing, but no luck after a few hours of different kinds of annotations. I think it would be useful to add an example that covers this case to the repo. Note that if I change the type signature to the following my code works:

fn make_graphql_request(url: &str, query: &QueryBody<list_markets_id::Variables>) -> Response<list_markets_id::ResponseData>

Ejhfast avatar Aug 17 '19 16:08 Ejhfast

Thanks for writing up the issue - this should definitely be documented better. I don't have time to look into it in depth today, but to answer your last question ("What is the type that implements list_markets_id::Variables and list_markets_id::ResponseDara?"), it's the struct you annotated with #[derive(GraphQLQuery)] if you use the derive, or a generated struct with the name of your operation if you use the CLI.

So if your GraphQL file looks like this:

query ListMarketsId { ... }

the CLI will generate a struct called ListMarketsId that implements GraphQLQuery (and has the Variables and ResponseData associated types).

tomhoule avatar Aug 17 '19 18:08 tomhoule

I will come back to this and see how we can improve the situation, starting with better docs. Input and ideas are also very much appreciated :)

tomhoule avatar Aug 17 '19 18:08 tomhoule

Thanks @tomhoule, and also for the fast response! Any idea how I can add a type annotation that will make the above work? I did suspect that ListMarketsId was what implemented it, but am confused why Rust can't infer the G type in that case.

Ejhfast avatar Aug 17 '19 18:08 Ejhfast

Also yes, I'd be more than happy to add an example / contribute some docs after figuring this out completely!

Ejhfast avatar Aug 17 '19 18:08 Ejhfast

I just reread the issue: the problem is that make_graphql_request takes something that implements GraphQLQuery, but you are passing q, which is a QueryBody so inference is impossible. There are 2 ways to fix this:

  • Change the signature of make_graphql_request to also take a GraphQLQuery (the G parameter in your example) by value (it's an empty struct) and the associated variables separately, then call build_query from inside make_graphql_request
  • Explicitly providing make_graphql_request with the type (circumventing inference), so it would look something like:
let response = make_graphql_query::<ListMarketId>(&"...", &q);

I have to go away from my computer now so I won't be able to reply today, I hope this helps!

tomhoule avatar Aug 17 '19 18:08 tomhoule

Thank you! This is perfect.

I had thought about option (1) but wanted to avoid that approach. Explicitly providing the type on the call is exactly what I was looking for, but I am new enough to Rust that your approach was not obvious to me. Just tested it and it works :)

Happy to circle back on contributing some documentation on this later!

Ejhfast avatar Aug 17 '19 18:08 Ejhfast