如何正确使用GraphQL订阅?

7
我有一个由GraphQL驱动的应用程序。查询和修改部分运行良好。我尝试添加GraphQL订阅。
服务器GraphQL订阅代码的灵感来自apollographql/subscriptions-transport-ws自述文件中的演示。
请查看代码中的注释以获取更多详细信息。
import Koa from 'koa';
import Router from 'koa-router';
import graphqlHTTP from 'koa-graphql';
import asyncify from 'callback-to-async-iterator';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import firebase from 'firebase-admin';
import { execute, subscribe } from 'graphql';
import { GraphQLObjectType, GraphQLString } from 'graphql';

const MeType = new GraphQLObjectType({
  name: 'Me',
  fields: () => ({
    name: { type: GraphQLString },
    // ...
  }),
});

const listenMe = async (callback) => {
  // Below the firebase API returns real-time data
  return firebase
    .database()
    .ref('/users/123')
    .on('value', (snapshot) => {
      // snapshot.val() returns an Object including name field.
      // Here I tested is correct, it always returns { name: 'Rose', ... }
      // when some other fields inside got updated in database.
      return callback(snapshot.val());
    });
};

const Subscription = new GraphQLObjectType({
  name: 'Subscription',
  fields: () => ({
    meChanged: {
      type: MeType,
      subscribe: () => asyncify(listenMe),
    },
  }),
});

const schema = new GraphQLSchema({
  query: Query,
  mutation: Mutation,
  subscription: Subscription,
});

const app = new Koa();
app
  .use(new Router()
    .post('/graphql', async (ctx) => {
      // ...

      await graphqlHTTP({
        schema,
        graphiql: true,
      })(ctx);
    })
    .routes());

const server = app.listen(3009);

SubscriptionServer.create(
  {
    schema,
    execute,
    subscribe,
  },
  {
    server,
    path: '/subscriptions',
  },
);

我正在使用Altair GraphQL Client进行测试,因为它支持GraphQL订阅。

enter image description here

如屏幕截图所示,当数据库中的数据发生更改时,它确实会获取新数据。
但是,meChangednull,并且不会抛出任何错误。有什么想法吗?谢谢

我不知道alway是什么意思,而且你没有在Chrome Dev Tools的网络面板中包含错误消息,因此诊断你的问题很困难。但是,你看过这个吗:https://dev59.com/kFMI5IYBdhLWcg3wZKng - Preston
@Preston 谢谢!我刚刚更新了标题。我希望我能够发布Chrome控制台错误消息,但是由于缺乏使用类似Apollo这样的框架的GraphQL订阅的文档,我尚未开始为客户端构建订阅部分。这就是为什么我使用Altair GraphQL客户端作为起点来帮助我理解GraphQL订阅的工作原理。 - Hongbo Miao
1个回答

5

终于有一个新的库可以在不使用完整的 Apollo 框架的情况下完成工作。

https://github.com/enisdenjo/graphql-ws

这里是我已经成功运行的代码:

服务器端 (GraphQL Schema Definition Language)

import { useServer } from 'graphql-ws/lib/use/ws';
import WebSocket from 'ws';
import { buildSchema } from 'graphql';

const schema = buildSchema(`
  type Subscription {
    greeting: String
  }
`);

const roots = {
  subscription: {
    greeting: async function* sayHiIn5Languages() {
      for (const hi of ['Hi', 'Bonjour', 'Hola', 'Ciao', 'Zdravo']) {
        yield { greeting: hi };
      }
    },
  },
};

const wsServer = new ws.Server({
  server, // Your HTTP server
  path: '/graphql',
});
useServer(
  {
    schema,
    execute,
    subscribe,
    roots,
  },
  wsServer
);

服务器(GraphQL.js GraphQLSchema 对象方式)

import { execute, subscribe, GraphQLObjectType, GraphQLSchema, GraphQLString } from 'graphql';
import { useServer } from 'graphql-ws/lib/use/ws';
import WebSocket from 'ws';
import { PubSub } from 'graphql-subscriptions';

const pubsub = new PubSub();

const subscription = new GraphQLObjectType({
  name: 'Subscription',
  fields: {
    greeting: {
      type: GraphQLString,
      resolve: (source) => {
        if (source instanceof Error) {
          throw source;
        }
        return source.greeting;
      },
      subscribe: () => {
        return pubsub.asyncIterator('greeting');
      },
    },
  },
});

const schema = new GraphQLSchema({
  query,
  mutation,
  subscription,
});

setInterval(() => {
  pubsub.publish('greeting', {
    greeting: 'Bonjour',
  });
}, 1000);

const wsServer = new ws.Server({
  server, // Your HTTP server
  path: '/graphql',
});
useServer(
  {
    schema,
    execute,
    subscribe,
    roots,
  },
  wsServer
);

客户端

import { createClient } from 'graphql-ws';

const client = createClient({
  url: 'wss://localhost:5000/graphql',
});

client.subscribe(
  {
    query: 'subscription { greeting }',
  },
  {
    next: (data) => {
      console.log('data', data);
    },
    error: (error) => {
      console.error('error', error);
    },
    complete: () => {
      console.log('no more greetings');
    },
  }
);

声明:我与该库无关。


@sagg1295 请查看 https://github.com/Hongbo-Miao/hongbomiao.com/blob/main/api/src/graphQL/queries/subscription.ts,这可能会对你有所帮助。它是一个完整的工作演示。 - Hongbo Miao
谢谢您。我想澄清一件事,在上面的链接中,您使用了 graphql-subscriptions 模块。必须使用它来进行订阅吗?我们不能只使用 graphql-ws、ws 和 graphql 来进行 GraphQL 订阅吗?因为我正在遵循 GitHub 页面上给出的 graphql-ws 代码,但它不起作用。 如果您愿意,我可以分享我的 GitHub 存储库,如果您能够查看一下,它只有两个具有逻辑的文件。 - sagg1295
@sagg1295 在答案中使用的 GraphQL Schema Definition Language 方法也是使用 graphql-subscriptionsgraphql-ws,这是相同的。 - Hongbo Miao
@sagg1295 graphql-wssubscriptions-transport-ws 处于同一级别。而 graphql-subscriptions 则是不同级别的东西。 - Hongbo Miao
1
你的第一个例子中有 import WebSocket from 'ws';,但是你后面使用了 ws.Server,这是正确的吗? - justin.m.chase
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接