0

I am using apollo-server-express for graphql back-end. I am going to process only mutations there, but I want to redirect query and subscription on hasura by means of schema stitching with introspection. Queries through apollo-server to hasura are working fine and returning the expected data.

But subscriptions are not working and I am getting this error: " Expected Iterable, but did not find one for field subscription_root.users". error from console

And besides, server hasura is receiving events: events from hasura server

But apollo-server resents the answer from hasura. It is not the first day I suffer with this and I can not understand what the problem is.

In the editor hasura subscriptions work.

Link to full code

If you need any additional info, I will gladly provide it to you.

import {
  introspectSchema,
  makeExecutableSchema,
  makeRemoteExecutableSchema,
  mergeSchemas,
  transformSchema,
  FilterRootFields
} from 'graphql-tools';
import { HttpLink } from 'apollo-link-http';
import nodeFetch from 'node-fetch';
import { resolvers } from './resolvers';
import { hasRoleResolver } from './directives';
import { typeDefs } from './types';
import { WebSocketLink } from 'apollo-link-ws';
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import * as ws from 'ws';
import { OperationTypeNode } from 'graphql';

interface IDefinitionsParams {
  operation?: OperationTypeNode,
  kind: 'OperationDefinition' | 'FragmentDefinition'
}

const wsurl = 'ws://graphql-engine:8080/v1alpha1/graphql';

const getWsClient = function (wsurl: string) {
  const client = new SubscriptionClient(wsurl, {
    reconnect: true,
    lazy: true
  }, ws);
  return client;
};

const wsLink = new WebSocketLink(getWsClient(wsurl));

const createRemoteSchema = async () => {
  const httpLink = new HttpLink({
    uri: 'http://graphql-engine:8080/v1alpha1/graphql',
    fetch: (nodeFetch as any)
  });

  const link = split(
    ({ query }) => {
      const { kind, operation }: IDefinitionsParams = getMainDefinition(query);
      console.log('kind = ', kind, 'operation = ', operation);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
  );

  const remoteSchema = await introspectSchema(link);
  const remoteExecutableSchema = makeRemoteExecutableSchema({
    link,
    schema: remoteSchema
  });

  const renamedSchema = transformSchema(
    remoteExecutableSchema,
    [
      new FilterRootFields((operation, fieldName) => {
        return (operation === 'Mutation') ? false : true; //  && fieldName === 'password'
      })
    ]
  );
  return renamedSchema;
};

export const createNewSchema = async () => {
  const hasuraExecutableSchema = await createRemoteSchema();
  const apolloSchema = makeExecutableSchema({
    typeDefs,
    resolvers,
    directiveResolvers: {
      hasRole: hasRoleResolver
    }
  });
  return mergeSchemas({
    schemas: [
      hasuraExecutableSchema,
      apolloSchema
    ]
  });
};

2 Answers2

0

Fixed by installing graphql-tools 4th version. It tutns out the editor did not even notice that I do not have this dependency and simply took the version of node_modules, which was installed by some other package. Problem was with version 3.x. Pull request is where the bug was fixed.

0

I had the same problem, different cause and solution.

My subscription was working well, until I introduced the 'resolve' key in my subscription resolver:

Here is the 'Subscription' part of My resolver:

Subscription: {
    mySubName: {
      resolve: (payload) => {
        console.log('In mySubName resolver, payload:',payload)
        return payload;
      },
      subscribe:() => pubSub.asyncIterator(['requestsIncomplete']),
      // )
    },

The console.log proved the resolve() function was being called with a well structured payload (shaped the same as my Schema definiton - specifically the an object with a key named after the graphQL Subscriber, pointing to an array (array is an iterable):

In mySubName resolver, payload: { mySubName:
   [ { id: 41,
       ...,
      },
      {...},
      {...}
      ...
      ...
   ]

Even though I was returning that same unadulterated object, it caused the error expected Iterable, but did not find one for field "Subscription.mySubName"

When I commented out that resolve function all together, the subscription worked, which is further evidence that my payload was well structured, with the right key pointing to an iterable.

I must be mis-using the resolve field. From https://www.apollographql.com/docs/graphql-subscriptions/subscriptions-to-schema/

When using subscribe field, it's also possible to manipulate the event payload before running it through the GraphQL execution engine.

Add resolve method near your subscribe and change the payload as you wish

so I am not sure how to properly use that function, specifically don't know what shape object to return from it, but using it as above breaks the subscription in the same manner you describe in your question.

I was already using graphql-tools 4.0.0, I upgraded to 4.0.8 but it made no difference.

NULL pointer
  • 1,116
  • 1
  • 14
  • 27