lynxtaa / awesome-graphql-client

GraphQL Client with file upload support for NodeJS and browser
https://npm.im/awesome-graphql-client
MIT License
58 stars 7 forks source link

Support for persisted queries? #88

Open onionhammer opened 10 months ago

onionhammer commented 10 months ago

Is your feature request related to a problem? Please describe. It would be great if this library support persisted queries, which means the server already knows the query the client is sending, and the client merely passes an "id" in the query string (GET) or "id" in the body (POST) rather than a query in the body

Describe the solution you'd like A way for graphClient.request to pass an id corresponding to a query, or an alternative graphClient.requestId() where the first arg is an ID rather than the query itself

Describe alternatives you've considered Alternatively, the library could provide a way to customize how operations are serialized, instead of just a hard-coded JSON.stringify(...)

lynxtaa commented 10 months ago

Thanks for the suggestions @onionhammer !

I think it could be done by providing a custom fetch function? I see that approach suggested for graphql-request library https://github.com/jasonkuhrt/graphql-request/issues/269#issuecomment-1703936594

onionhammer commented 10 months ago

Thanks for the suggestions @onionhammer !

I think it could be done by providing a custom fetch function? I see that approach suggested for graphql-request library https://github.com/jasonkuhrt/graphql-request/issues/269#issuecomment-1703936594

Unless I'm mistaken, it can be done with a custom fetch, but then one might as well not even use this library at all because you would have to reimplement pretty much everything

Check out the PR please, it would greatly improve the flexibility while also still allowing a user to utilize the parts of this library that are great

lynxtaa commented 10 months ago

My suggestion is to run client.request() by passing the id string to it instead of query. Then deserialize the body in custom fetch function and serialize it again with id field. Something like this

async function customFetch(url, init) {
  const { query, ...rest } = JSON.parse(init.body)
  return fetch(url, { ...init, body:  JSON.stringify({  ...rest, id: query }) })
}
onionhammer commented 10 months ago

@lynxtaa This works if the body is JSON and not FormData, which is not guaranteed. What is your objection to making the serialization itself more flexible? Perhaps you have suggestions on how I can change the PR to be less objectionable?

lynxtaa commented 10 months ago

This works if the body is JSON and not FormData, which is not guaranteed

It's also possible to get an operations field if body is not a string but FormData.

What is your objection to making the serialization itself more flexible?

I'm really grateful for your desire to improve the dev experience of this library and happy that you're using it :) I will think on implementing something like hooks to make it more flexible. So that the list of supported options and complexity would not grow and it'll be easier to support and maintain.

I would suggest using a custom fetch function for now. I know this solution is far from great, sorry for that. I'll keep this issue opened

onionhammer commented 10 months ago

Thank you for the suggestion, if it is not too inefficient, I will take your recommendation. Otherwise, I may just end up forking this library.

onionhammer commented 10 months ago

Here is what this workaround/hack looks like to anyone curious:

  fetch: (url, init) => {
    // eslint-disable-next-line prefer-const
    let { body, ...rest } = init;

    if (body instanceof FormData) {
      const operationsString = body.get("operations");
      if (!operationsString)
        throw new Error("Missing operations");

      const operations = JSON.parse(operationsString as string);
      const id = operations.query;
      body.set("operations", JSON.stringify({ ...operations, query: undefined, id }));
    }
    else {
      const parsed = JSON.parse(body);

      if (parsed.query) {
        parsed.id = parsed.query;
        delete parsed.query;
      }

      body = JSON.stringify(parsed);
    }

    return fetch(url, { ...rest, body });
  },