使用 Express-GraphQL 和 React-Apollo 订阅 GraphQL

Posted

技术标签:

【中文标题】使用 Express-GraphQL 和 React-Apollo 订阅 GraphQL【英文标题】:GraphQL Subscriptions with Express-GraphQL and React-Apollo 【发布时间】:2017-09-01 17:41:40 【问题描述】:

我已经按照 Apollo 的文档在客户端和服务器上设置 GraphQL 订阅,虽然我已经完成了 90%,但我不知道如何设置订阅通道以及如何将突变连接到这些通道这样无论何时发生突变,服务器都会将新数据推送到客户端。 (对于内容,我正在制作一个 Reddit 克隆,人们在其中发布主题和其他人对其发表评论。因此,当您看到“主题”或“主题列表”时,将它们视为帖子。)

到目前为止,我已经成功设置了 Apollo Client 进行订阅:

const wsClient = new SubscriptionClient('ws://localhost:3001/subscriptions', 
  reconnect: true
);

const networkInterface = createNetworkInterface(
    uri: '/graphql',
    opts: 
        credentials: 'same-origin'
    
);

const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
  networkInterface,
  wsClient,
);

const client = new ApolloClient(
    networkInterface: networkInterfaceWithSubscriptions,
    dataIdFromObject: o => o.id
);

我还为订阅设置了后端。这是我的 server.js 文件:

//===========================================================
//Subscription Managaer
//===========================================================
const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager(
  schema: schema,
  pubsub: pubsub
);

//=====================================
//WebSocket + Express Server
//=====================================

const server = createServer(app);

//setup listening port
server.listen(3001, ()=>
    new SubscriptionServer(
    
        subscriptionManager: subscriptionManager,
        onConnect: (connectionParams, webSocket) => 
            console.log('Websocket connection established');
        ,
        onSubscribe: (message, params, webSocket) => 
            console.log("The client has been subscribed", message, params);
        ,
        onUnsubsribe: (webSocket) => 
            console.log("Now unsubscribed");
        ,
        onDisconnect: (webSocket) => 
            console.log('Now disconnected');
        
    , 
    
        server: server,
        path: '/subscriptions',
    );
    console.log('Server is hot my man!');
)

我知道这些都是成功的,因为我在终端中记录了“Websocket 连接已建立”消息。

接下来是实际订阅 - 我创建了订阅模式类型(就像查询和突变一样):

const SubscriptionType = new GraphQLObjectType(
    name: 'Subscription',
    fields: () => (
        topicAdded: 
            type: TopicType,
            args: repoFullName: type: GraphQLString, //I don't understand what repoFullName is - I was trying to follow the Apollo docs, but they never specified that
            resolve(parentValue, args)
                return parentValue;
            

        
    )
);

module.exports = SubscriptionType;

并将其合并到我的根架构中。因此,当我查看 GraphiQL 时,我看到:Docs 侧菜单中提供了此订阅 My GraphiQIL UI showing the subscriptionSchema successfully

在我的 React 组件中,我使用 Apollo 的 subscribeToMore 方法成功地“订阅”了它:

const TOPICS_SUBSCRIPTION = gql`
    subscription OnTopicAdded($repoFullName: String)
        topicAdded(repoFullName: $repoFullName)
            id
        
    
`;

class TopicList extends Component 
    componentDidMount() 
        this.createMessageSubscription = this.props.data.subscribeToMore(
          document: TOPICS_SUBSCRIPTION,
          // updateQuery: (previousState, subscriptionData) => 
          //   const newTopic = subscriptionData.data.Topic.node
          //   const topics = previousState.findTopics.concat([newTopic])
          //   return 
          //     findTopics: topics
          //   
          // ,
          onError: (err) => console.error(err)
        )

       //...

我在终端中收到“客户已订阅”消息。但这就是我卡住的地方。我已经阅读了有关 SubscriptionManager 的 SetupFunction 的信息,但这不包含在 Apollo 的文档中。而且我找不到如何将“createTopic”突变映射到此订阅,以便每当有人添加新主题时,它都会在 TopicList 中弹出。

我意识到这真的很长,但我一直在努力弄清楚下一步是什么。任何帮助将非常感激!!感谢您的阅读!

【问题讨论】:

【参考方案1】:

是的,您缺少设置功能。您可以查看此链接GraphQL subscription docu 或此example。

它应该如何工作: 首先,您需要发布更改数据的通道。在您的情况下,它可能如下所示:

const manager = new sub.SubscriptionManager(
  schema,
  pubSub,

  setupFunctions: 
    topicAdded: (options, args) => ( // name of your graphQL subscription
      topicAddedChannel:  // name of your pubsub publish-tag
        filter: (topic) => 
          console.log(topic); //should always show you the new topic if there is a subscribed client
          return true; // You might want to add a filter. Maybe for guest users or so
        ,
      ,
    ),
  ,
);

在这里您可以看到订阅中需要 args: repoFullName: type: GraphQLString 参数。如果要过滤依赖于“repoName”的订阅。这意味着只有订阅了“repoName”作为参数的客户端才能获得更新。

接下来,您需要一个调用pubsub.publish 函数的地方。在您的情况下,添加主题突变已经过去。可能看起来像这样:

...

const topic = new Topic(/* args */);
topic.save((error, topic) => 
  if (!error) 
    pubsub.publish("topicAddedChannel", topic);
  
  ...
);


....

【讨论】:

感谢 Locco0_0!但即使在我尝试添加新评论之前,我在控制台中收到此错误:Uncaught TypeError: Cannot read property 'handler' of undefined at WebSocket.client.onmessage 以及:从 onSubscribe 返回的参数无效!返回值必须是一个对象!”有关更多参考,这里是我现在的 addTopic 突变(在代码笔中,因为我无法将其放入此处以 char 限制):link 我仍然可以添加一个主题,但我必须像往常一样刷新页面以使其显示在列表中。有什么想法吗? 订阅定义看起来不错。 TopicType 是否已定义?在您的情况下,您可以删除 args 参数,因为您没有要传递给订阅的参数。所以你可以在这里删除它const TOPICS_SUBSCRIPTION = gql subscription OnTopicAdded topicAdded id ; 第一个错误对我来说不是很清楚,但你可以在componentWillReceiveProps(newProps) 函数中添加订阅并检查订阅查询是否已加载 我刚刚在代码链接中看到您设置了new pubsub()。在这里你必须使用你添加到订阅管理器的相同

以上是关于使用 Express-GraphQL 和 React-Apollo 订阅 GraphQL的主要内容,如果未能解决你的问题,请参考以下文章

使用 express-graphql 和 react-apollo 处理错误

使用 Express-GraphQL 和 React-Apollo 订阅 GraphQL

Express-GraphQL 上的多个过滤器

使用 Express-GraphQL 的 GraphQL 订阅

如何使用 express-graphql 引发多个错误?

Graphql 突变查询不适用于 express-graphql