graphql-hive / gateway

GraphQL gateway that can act as a Federation Gateway or a Proxy Gateway for any GraphQL service.
https://the-guild.dev/graphql/hive/docs/gateway
MIT License
13 stars 2 forks source link

Hive Gateway: Subscription field must return Async Iterable. Received: undefined #136

Open masesor opened 1 week ago

masesor commented 1 week ago

Hi, I have a subgraph that follows this getting started example: https://the-guild.dev/graphql/yoga-server/docs/features/subscriptions#getting-started

type Query {
  hello: String
}

type Subscription {
  countdown(from: Int!): Int!
}
import { createServer } from 'node:http';
import { setTimeout as setTimeout$ } from 'node:timers/promises';

import { createSchema, createYoga } from 'graphql-yoga';

// Provide your schema
const yoga = createYoga({
  schema: createSchema({
    typeDefs: /* GraphQL */ `
      type Query {
        hello: String
      }

      type Subscription {
        countdown(from: Int!): Int!
      }
    `,
    resolvers: {
      Query: {
        hello: () => 'world',
      },
      Subscription: {
        countdown: {
          // This will return the value on every 1 sec until it reaches 0
          async *subscribe(_, { from }) {
            for (let i = from; i >= 0; i--) {
              await setTimeout$(1000);
              yield { countdown: i };
            }
          },
        },
      },
    },
  }),
});

const server = createServer(yoga);
server.listen(21204, () => {
  console.info('Server is running on http://localhost:21204/graphql');
});

This works perfectly fine when connecting to this subgraph and running the subscription in graphiql.

I have a supergraph component that is defined with:

import { createPrefixTransform, defineConfig, loadGraphQLHTTPSubgraph } from '@graphql-mesh/compose-cli';

export const composeConfig = defineConfig({
  output: '_generated/supergraph.graphql',
  subgraphs: [
    {
      sourceHandler: loadGraphQLHTTPSubgraph('my-subgraph', {
        endpoint: 'http://localhost:21204/graphql',
        source: 'path/to/subgraph/schema.graphql',
      }),
      transforms: [
        createPrefixTransform({
          value: 'test_',
          includeRootOperations: true,
        }),
      ],
    },
  ],
});

Then I have my Hive Gateway:

gateway.config.ts


export const gatewayConfig = defineConfig({
  supergraph: async () => {
    try {
      const data = await fs.readFile(supergraphFile, 'utf-8');
      return data;
    } catch (err) {
      console.error('Error reading supergraph file:', err);
      throw err;
    }
  },
  playground: true,
  introspection: true,
  browser: true,
  healthCheckEndpoint: '/health',
  host: '0.0.0.0',
  port,
});

Running in docker-compose:

services:
  graphql-gateway:
    build:
      context: ..
      dockerfile: ./graphql-gateway/Dockerfile
    container_name: graphql-gateway
    ports:
      - '4000:4000'
    restart: always
    networks:
      - internal-network-proxy
    environment:
      SUPERGRAPH_FILE: ./supergraph.graphql
      SUPERGRAPH_PORT: 4000
      DEBUG: 1 # Enable if you require more detailed logs

networks:
  internal-network-proxy:
    external:
      name: internal_network

The Dockerfile copies the supergraph.graphql and gateway config into the container

When executing the query in the Hive Gateway GraphiQL, I get:

{
  "errors": [
    {
      "message": "Subscription field must return Async Iterable. Received: undefined.",
      "locations": [
        {
          "line": 1,
          "column": 29
        }
      ],
      "path": [
        "lt_countdown"
      ]
    }
  ]
}

From my understanding from the documentation, there doesn't seem to be any extra configuration that I need to set in the Hive Gateway or the Mesh Compose supergraph for subscriptions using SSE. Am I missing something?

Versions:

"graphql-yoga": "^5.10.2"
"@graphql-mesh/compose-cli": "^1.0.2"
Hive Gateway Docker: ghcr.io/ardatan/hive-gateway:latest

Thanks!

Edit: It seems if I remove includeRootOperations: true it works, however I do need to have prefixes on the subscription. Not sure why the prefix is breaking it