logaretm / villus

🏎 A tiny and fast GraphQL client for Vue.js
https://villus.dev
MIT License
790 stars 31 forks source link

graphql-ws incompatible types #133

Closed JarvisH closed 3 years ago

JarvisH commented 3 years ago

First up, thanks for fixing all other reported issues so quickly!

According to the documentation it should be possible to use graphql-ws as a websocket client for subscriptions.

Using the below example in a typescript project shows the followin TS errors:

Error 1:

Argument of type '(operation: any) => { subscribe: (obs: any) => void; }' is not assignable to parameter of type 'SubscriptionForwarder<any>'.
  Call signature return types '{ subscribe: (obs: any) => void; }' and 'ObservableLike<StandardOperationResult<any>>' are incompatible.
    The types returned by 'subscribe(...)' are incompatible between these types.
      Type 'void' is not assignable to type 'Unsubscribable'.

... and error 2:

Type 'string | DocumentNode | TypedDocumentNode<unknown, QueryVariables>' is not assignable to type 'string'.
  Type 'DocumentNode' is not assignable to type 'string'.
import { createClient, handleSubscriptions, SubscriptionForwarder } from 'villus'
import { createClient as graphqlWsCreateClient } from 'graphql-ws';

const wsClient = graphqlWsCreateClient ({
  url: 'ws://localhost:9005/graphql',
});
const subscriptionForwarder: SubscriptionForwarder = operation => {
  return {
    // Error 1: Villus TS expects subscribe to return type `Unsubscribable`
    // but `graphql-ws` returns type void
    subscribe: obs => {
      wsClient.subscribe(
        {
          // Error 2: Villus passes string | DocumentNode etc. here,
          // graphql-ws only allows string
          query: operation.query,
        },
        obs
      );
    },
  };
};

const client = createClient({
  url: 'http://localhost:4000/graphql',
  use: [handleSubscriptions(subscriptionForwarder), ...defaultPlugins()]
})

Am I doing something wrong here?

logaretm commented 3 years ago

There is no way villus can support all types of subscription clients, so you need to follow the interfaces defined by villus.

Villus expects the subscribe method to return an "Unsubscribe" object, so you need to return it manually.

As for the query type, you can use graphql to convert it to a string, I haven't decided yet if villus should do that or not because it has no control over the transport layer for subscriptions unlike queries/mutations.

Again

import { print } from 'graphql';

const subscriptionForwarder: SubscriptionForwarder = operation => {
  return {
    subscribe: obs => {
      wsClient.subscribe(
        {
          query: typeof operation.query === 'string' ? operation.query : print(operation.query),
        },
        obs
      );

      return {
        unsubscribe: () => wsClient.dispose(),
      }
    },
  };
};