【问题标题】:How to use GraphQL subscription correctly?如何正确使用 GraphQL 订阅?
【发布时间】:2019-11-27 23:07:02
【问题描述】:

我有一个支持 GraphQL 的应用程序。查询和变异部分运行良好。我尝试添加 GraphQL 订阅。

服务器 GraphQL 订阅部分代码的灵感来自 apollographql/subscriptions-transport-ws 自述文件中的演示。

请查看代码中的 cmets 了解更多详情。

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 订阅。

如截图所示,每次数据库中的数据发生变化时,它都会获取新数据。

但是,meChangednull,它不会引发任何错误。任何想法?谢谢

【问题讨论】:

  • 我不知道总是意味着什么,并且您没有在 Chrome 开发工具的网络面板中包含错误消息,因此很难诊断您的问题。然而,你有没有看过这个:stackoverflow.com/questions/56319137/…
  • @Preston 谢谢!刚刚更新了标题。我希望我可以发布 Chrome 控制台错误消息,但我还没有开始为客户端构建订阅部分,因为它缺少使用 GraphQL 订阅的文档,而没有像 Apollo 这样的任何框架。这就是为什么我使用 Altair GraphQL Client 作为起点来帮助我了解 GraphQL 订阅的工作原理。

标签: javascript graphql graphql-js koa graphql-subscriptions


【解决方案1】:

终于有一个新库可以在没有完整 Apollo 框架的情况下完成这项工作。

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

这是我成功的代码:

服务器(GraphQL 架构定义语言)

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');
    },
  }
);

披露:我与图书馆无关。

【讨论】:

  • 我尝试了第一台服务器,即(服务器(GraphQL 架构定义语言))和客户端代码,因为它是做一个 POC,但我收到以下错误:-“订阅字段必须返回异步 Iterable。收到:{}。”。您能否让我知道我可能做错了什么,因为我是 graphql 订阅中的菜鸟
  • @sagg1295 检查github.com/Hongbo-Miao/hongbomiao.com/blob/main/api/src/graphQL/… 这可能会对您有所帮助。这是一个完整的工作演示。
  • 感谢您。我想在您使用 graphql-subscriptions 模块的上述链接中澄清一件事。是否有必要使用它进行订阅?我们不能只用graphql-ws、ws和graphql来做graphql订阅吗?因为我正在关注graphql-ws的github页面上给出的代码。那行不通。如果你愿意,我也可以分享我的 github 存储库,如果你可以通过它只有两个具有逻辑的文件
  • @sagg1295 答案中的 GraphQL Schema Definition Language 方式也是使用graphql-subscriptionsgraphql-ws,这是一样的。
  • 但是当我在 google 上搜索在 graphql 中进行订阅时,我也发现这个 graphql-subscriptionsgraphql-ws 是分开的。可能我错了。我认为这两种方法都是在graphql中订阅的不同方法
猜你喜欢
  • 2021-01-26
  • 2019-09-27
  • 2020-11-17
  • 1970-01-01
  • 2019-03-01
  • 2018-12-09
  • 2018-02-25
  • 2019-08-14
  • 2020-03-27
相关资源
最近更新 更多