MichalLytek / type-graphql

Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
https://typegraphql.com
MIT License
8.02k stars 675 forks source link

Error: Subscription field must return Async Iterable. Received: undefined. #1362

Open V1os opened 1 year ago

V1os commented 1 year ago

Describe the Bug Error after subscription client.

To Reproduce

/// server
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
import { useServer } from 'graphql-ws/lib/use/ws';
import { createServer } from 'http';
import { WebSocketServer } from 'ws';
/// ...

  const app = express();
  const httpServer = createServer(app);
  const wsServer = new WebSocketServer({
    server: httpServer,
    path: '/',
  })

  const schema = buildFederatedSchema({ orphanedTypes, resolvers, pubSub })
  const serverCleanup = useServer({ schema }, wsServer);
  const server = new ApolloServer({ schema });

  await server.start();
  server.applyMiddleware({ app, path: '/' });
  httpServer.listen({ port }, ... );

/// resolver
import { withFilter } from 'graphql-subscriptions';
import {
  Resolver,
  Root,
  Args,
  Subscription,
} from 'type-graphql';
/// ...

@Resolver()
export class SubscriptionResolver {
  @Subscription(() => Notification, {
    subscribe: withFilter(
      () => pubSub.asyncIterator(topics),
      (root, args) => {
        return args.type === root.type;
      },
    ),

    // subscribe() {
    //   return pubSub.asyncIterator(topics);
    // },

    // topics,
    // topics: ({ args }) => args.type,

    // filter: ({ args, payload }) => {
    //   return args.type === payload.type;
    // },
  })
  notifications(@Root() payload: NotificationPayload, @Args() args: NotificationArgs) {
    return {
      type: args.typeNotify,
      date: payload.date,
      meta: payload?.meta,
    };
  }

Expected Behavior After execution of subscription operation on Studio ApolloGraphql sandbox, i expect to connect to the socket, and receive messages on the emission of events. But get the error subscribe

Logs

...
🚀 Query endpoint ready at http://localhost:3008/
🚀 Subscriptions ready at ws://localhost:3008/
Internal error occurred during message handling. Please check your implementation. Error: Subscription field must return Async Iterable. Received: undefined.
    at createSourceEventStream (/***/node_modules/graphql/execution/subscribe.js:165:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async subscribe (/***/node_modules/graphql/execution/subscribe.js:67:26)
    at async onMessage (/***/node_modules/graphql-ws/lib/server.js:191:51)
    at async WebSocket.<anonymous> (/***/node_modules/graphql-ws/lib/use/ws.js:83:21)

Environment (please complete the following information):

Additional Context Add any other context about the problem here.

MichalLytek commented 1 year ago

Please create a PR with a failing test case. I can't guess from your snippets what's wrong with your apollo server, federation or server configuration.

carlocorradini commented 1 year ago

@V1os Have you added the correct TypeScript compiler options as shown in TypeGraphQL's documentation. Due to using the graphql-subscription dependency that relies on an AsyncIterator, we may also have to provide the esnext.asynciterable to the lib option:

{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "lib": ["es2018", "esnext.asynciterable"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
V1os commented 1 year ago

@carlocorradini hi. Yee, i provided needs options ts, but i think that it is some little thing, which is hard to see at first sight.

carlocorradini commented 1 year ago

@V1os Can you share the project or a failing example?

V1os commented 1 year ago

@carlocorradini I think how to do it ... for now, at most, this is the general view of the server and resolver. By the way, i builded scheme for federation service

carlocorradini commented 1 year ago

@V1os Federation version 1 or version 2?

V1os commented 1 year ago

2

carlocorradini commented 1 year ago

Found the issue, Federation 2 requires graphql >= 16 while TypeGraphQL requires graphql < 16. You can follow TypeGraphQL v2.0 with support for a newer version of graphql in this pull request. If you really want to use Apollo Federation V2 you must have a separate package with graphql >= 16 and then import it in your main project with graphql < 16. If you don't do something crazy, it works flawlessly. Take a look at my example here.

V1os commented 1 year ago

@carlocorradini tnx bro, i will write about the status.

carlocorradini commented 1 year ago

@V1os Can we close this issue? 🥳

V1os commented 1 year ago

Yep, there is a reason in version graphql package. Thank you

V1os commented 1 year ago

though, this is not version ( i take 15.8.0, from example, but still not working

carlocorradini commented 1 year ago

Did you created a different project with graphql > = 16 (different package.json) and imported in main project with graphql < 16

V1os commented 1 year ago

it is a lerna workspace (subscription service) with his package, and graphql@15.8.0

carlocorradini commented 1 year ago

Unfortunately, without the code or a minimal example, I can't determine what the cause could be.

exaucae commented 1 year ago

I experience a variant of this issue. My error message is:

Error: Subscription field must return Async Iterable. Received: null

It happens when there's at least 1h uninterupted connection between client and server I reported it in graphql-ws ( https://github.com/enisdenjo/graphql-ws/issues/418 ) but seems more appropriate here?

electrovir commented 1 year ago

This error happens to me when my authChecker returns false for the subscription. I want to hide the error (since it's not an error that an unauthorized subscription is blocked) but can't figure out where to do that...

electrovir commented 1 year ago

Workaround for authChecker issue:

Instead of returning false in the authChecker callback, throw an error directly when auth fails (inside the authChecker callback). This prevents the irrelevant subscription error.

Edit: oof, this leaks stack traces to the frontend though 😕

dev-bhargav7 commented 5 months ago

I have my resolvers (and schema) separated by small files. In the case of resolvers, they are javascript files, with a final export default resolvers;, and that's the line I had forgotten.

I'll show the code just in case:

import { pubSub, names } from '~/src/config/subscriptions';
import { EzfacilityClientBooking } from '~/src/repositories';

const resolvers = {
  Subscription: {
    clientBookingsChanged: {
      subscribe: () => {
        const asyncIterator = pubSub.asyncIterator([names.CLIENT_BOOKINGS_CHANGED]);

        // Fetch immediately the data from EZ
        const clientBooking = new EzfacilityClientBooking();
        clientBooking.getAll();

        return asyncIterator;
      }
    }
  }
};

export default resolvers;
MichalLytek commented 5 months ago

@dev-bhargav7 Is this a TypeGraphQL code?