refinedev/refine

[FEAT] Support TypedDocumentNode for GraphQL data-provider

jamesdh opened this issue · 6 comments

Is your feature request related to a problem? Please describe.

Following up on @alicanerdurmaz recent excellent work in #5742, and influenced by the gql.tada's integration with other GraphQL clients, it would be very impressive if Refine were able to better leverage the TypedDocumentNode and could greatly reduce or even eliminate boilerplate/generated code.

Describe alternatives you've considered

No response

Additional context

I'm new to Refine and just becoming acquainted with how it works. Since @alicanerdurmaz added support for meta.gqlQuery I thought I'd give gql.tada a try. It works for validating/autocompleting the query, but as things currently exist with the GraphQL data-provider, it is unable to infer any type information from the TypedDocumentNode

Describe the thing to improve

Fully support TypedDocumentNode in addition to DocumentNode.

I might take a crude hack at this, but would really appreciate any input from any of the Refine contributors!

Hey @jamesdh, I'm not very experienced with GraphQL and graphql ecosystem much but I think I understood the request here 😅 I think this can be done if we update the meta.gqlMutation and meta.gqlQuery types with union TypedDocumentNode (from @graphql-typed-document-node/core, compatible with gql.tada) and TypedQueryDocumentNode (from graphql).

Then with couple of overloads I think we can infer types from queries 🤔 I couldn't find any usage of TypedQueryDocumentNode but I guess this should be added since graphql exports it 🤔 ResultOf or VariablesOf can be implemented for it as well but not interoperable.

// something like this
export type ResultOf<T> = T extends TypedQueryDocumentNode<infer ResultType, infer VariablesType> ? ResultType : never;

As a result I think we can achieve this:

type PostResponse = { id: BaseKey; name: string; };

const query = graphql(` ... `);

// `TData` will be inferred from `query`
- const { data } = useOne<PostResponse>({
+ const { data } = useOne({
  resource: "posts",
  meta: {
    gqlQuery: query,
  }
});

Is that what you had in mind? Do you have any more to add to the context of this to help us to get in the right direction? 🙏

Is that what you had in mind?

@aliemir yes, that's essentially it! I know some of the UI libraries also extend/wrap the data-provider (like MUI's useDataGrid) so I'm not sure how easy it would be for that change to extend into those.

Also, I'm not sure the significance but even though gql.tada draws inspiration from the TypedDocumentNode, they actually define it as a TadaDocumentNode

In any case, some base level of support for this would be EPIC!

@kitten any chance you could provide some guidance? 🙏 I've been away from GraphQL/TypeScript for too long and struggling with this so far, but it would be awesome to see gql.tada working in Refine!

I don't really have time to sift through and gather context here, sorry! I don't quite understand in what position your library is in, and you're welcome to ask a more specific question on our Discord, if it relates closer to how urql or gql.tada provide this integration.

The more important point is that gql.tada has an integration point where it provide both, a property shaped like @graphql-typed-document-node/core and a property shaped like graphql-js's (i.e. TypedQueryDocumentNode)

That's defined:

I don't have enough context to know whether you're a GraphQL types "consumer" (like @urql/core) or provider (like gql.tada)

There's a detailed explanation on being a consumer here: https://gql-tada.0no.co/guides/typed-documents#typeddocumentnode-types

But you can also look at how urql, villus, or @apollo/client do this.

Depending on how your GraphQL request API method/function is shaped you may also be interested in this helper in @urql/core regarding optional variables: https://github.com/urql-graphql/urql/blob/e5c32daa34a69ff0bfd8cb74dc693826d9294a21/packages/core/src/types.ts#L363-L404

A small side-note, assuming again you're looking to consume these types, is to try to refrain from exposing type helpers such as ResultOf and VariablesOf (etc).
All GraphQL type providers will likely expose this, and if you're exposing your own this may create confusion, so typically type-consumers (i.e. GraphQL clients) will not re-expose any helpers for those and only internally define types to consume GraphQL types.

The special types I've linked are basically unions of the two referenced libraries btw — meaning, they define the two different function properties that graphql-js and @graphql-typed-document-node/core define. The former is rarely used afaik, but it doesn't hurt to support it.

@kitten thank you for the guidance, that's more than I was hoping for!