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

Support for persisted queries?

Open onionhammer opened this issue 1 year ago • 7 comments

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

  • https://www.apollographql.com/docs/kotlin/advanced/persisted-queries/
  • https://chillicream.com/docs/hotchocolate/v13/performance/persisted-queries

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(...)

onionhammer avatar Dec 28 '23 16:12 onionhammer

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

lynxtaa avatar Jan 05 '24 21:01 lynxtaa

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

onionhammer avatar Jan 05 '24 21:01 onionhammer

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 }) })
}

lynxtaa avatar Jan 05 '24 21:01 lynxtaa

@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?

onionhammer avatar Jan 06 '24 13:01 onionhammer

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

lynxtaa avatar Jan 06 '24 14:01 lynxtaa

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 avatar Jan 06 '24 15:01 onionhammer

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 });
  },

onionhammer avatar Jan 08 '24 19:01 onionhammer