graphql 订阅返回空数据

Posted

技术标签:

【中文标题】graphql 订阅返回空数据【英文标题】:graphql subscription return null data 【发布时间】:2021-05-21 12:53:07 【问题描述】:

这是我的问题,在我的反应应用程序中,每当创建订单时,我都想获得该订单的订阅,称为 orderNotification,

在订单解析器中设置:

  Subscription: 
        orderNotification: 
            subscribe: (_, __,  pubsub ) => pubsub.asyncIterator(ORDER_NOTIFICATION)
        
    

变异:

Mutation: 
        async createOrder(_,  MakeOrderInput:  state, message, products, total  , context) 

            try 
                const userAuth = isAuth(context);
                const pubsub = context.pubsub;

                const newOrder = new Order(
                    state,
                    username: userAuth.username,
                    user: userAuth.id,
                    createdAt: new Date().toISOString(),
                    total,
                    message,
                    products,
                    userAddress: userAuth.address,
                );

           
                const index = products.findIndex(x => x.cost === 0);

                if (index != -1) 
                    const u = await User.findById( _id: userAuth.id );
                    await User.findByIdAndUpdate( _id: userAuth.id ,  points: u.points - 20 , (err, data) => 
                        if (err) 
                            console.log(err)
                         else 
                            console.log('fatto')
                        
                    );
                

                const order = await newOrder.save();

                pubsub.publish(ORDER_NOTIFICATION, 
                    orderNotification: order
                );

                return order;

             catch (err) 
                // throw new Error(err);
                console.log(err)
            
        ,

在 graphql Playground 中一切正常,但是当我必须在我的组件中获取并显示结果时,返回的数据为空:

import React from 'react'
import gql from 'graphql-tag';
import  useSubscription  from '@apollo/client';

import  Box  from 'grommet'

function SubscriptionOrder() 

    const  data, loading, error  = useSubscription(SUBSCRIPTION_USER_ORDER, 
        onSubscriptionData: (d) => console.log(d),
        onSubscriptionComplete: (da) => console.log(da)
    );

    // return null
    // console.log(data)

    return (
        <>
            <Box style= marginTop: '96px' >
                data && data.orderNotification ? (
                    <h1>hi: data.orderNotification.username</h1>
                ) : (
                        <h1>NO DATA</h1>
                    )
                
            </Box>
        </>
    )

;

const SUBSCRIPTION_USER_ORDER = gql`
  subscription orderNotification
    orderNotification 
        username
    
  
`;

export default SubscriptionOrder;

所以考虑到在操场上工作,错误可能在我的 ApolloClient 链接配置中:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import  ApolloClient  from 'apollo-client';
import  ApolloProvider  from '@apollo/react-hooks';
import  InMemoryCache  from 'apollo-cache-inmemory';
import  HttpLink  from 'apollo-link-http';
import  onError  from 'apollo-link-error';
import  ApolloLink, Observable, split  from 'apollo-link';

import  WebSocketLink  from 'apollo-link-ws';
import  getMainDefinition  from 'apollo-utilities';

import  TokenRefreshLink  from "apollo-link-token-refresh";
import jwtDecode from "jwt-decode";

import  getAccessToken, setAccessToken  from './accessToken';

import dotenv from 'dotenv/config.js'

const cache = new InMemoryCache();

const httpLink = new HttpLink(
  uri: process.env.NODE_ENV === 'development' ? `$process.env.REACT_APP_SERVER_DEV/graphql` : `$process.env.REACT_APP_SERVER_PRODUCTION/graphql`,
  credentials: "include",
);

const wsLink = new WebSocketLink(
  uri: process.env.NODE_ENV === 'development' ? `ws://$process.env.REACT_APP_SERVER_DEV_WS/graphql` : `ws://$process.env.REACT_APP_SERVER_PRODUCTION_WS/graphql`,
  options: 
    reconnect: true,
    lazy: true,
    inactivityTimeout: 1000,
  ,
  connectionCallback: err => 
    if (err) 
      console.log('Error Connecting to Subscriptions Server', err);
    
  
);


const splitLink = split(
  ( query ) => 
    const  kind, operation  = getMainDefinition(query);
    return kind === "OperationDefinition" && operation === "subscriptions";
  ,
  wsLink,
  httpLink
);

const requestLink = new ApolloLink(
  (operation, forward) =>
    new Observable(observer => 
      let handle;
      Promise.resolve(operation)
        .then(operation => 
          const accessToken = getAccessToken();
          if (accessToken) 
            operation.setContext(
              headers: 
                authorization: `Bearer $accessToken`
              ,
              fetchOptions: 
                credentials: 'include'
              
            );
          
        )
        .then(() => 
          handle = forward(operation).subscribe(
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer)
          );
        )
        .catch(observer.error.bind(observer));

      return () => 
        if (handle) handle.unsubscribe();
      ;
    )
);

const client = new ApolloClient(
  link: ApolloLink.from([
    new TokenRefreshLink(
      accessTokenField: "accessToken",
      isTokenValidOrUndefined: () => 
        const token = getAccessToken();

        if (!token) 
          return true;
        

        try 
          const  exp  = jwtDecode(token);
          if (Date.now() >= exp * 1000) 
            return false;
           else 
            return true;
          
         catch 
          return false;
        
      ,
      fetchAccessToken: () => 
        return fetch(process.env.NODE_ENV === 'development' ? `$process.env.REACT_APP_SERVER_DEV/refresh_token` : `$process.env.REACT_APP_SERVER_PRODUCTION/refresh_token`, 
          method: "POST",
          credentials: "include"
        );
      ,
      handleFetch: accessToken => 
        setAccessToken(accessToken);
      ,
      handleError: err => 
        console.warn("Your refresh token is invalid. Try to relogin");
        console.error(err);
      
    ),
    onError(( graphQLErrors, networkError ) => 
      console.log(graphQLErrors);
      console.log(networkError);
    ),
    requestLink,
    splitLink,
  ]),
  cache,
  connectToDevTools: true,
  credentials: 'include',
);



ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client=client>
      <App />
    </ApolloProvider>
  </React.StrictMode >,
  document.getElementById('root')
);

这是我的服务器:

import  PubSub  from 'graphql-subscriptions';
const pubsub = new PubSub();

const server = new ApolloServer(
    typeDefs,
    resolvers,
    context: ( req, res ) => ( req, res, pubsub ),
    introspection: true,
    cors: corsOptions,
);

server.applyMiddleware( app, cors: false );

const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);

httpServer.listen(PORT, () => 
    console.log(`???? Server ready at http://localhost:$PORT$server.graphqlPath`)
    console.log(`???? Subscriptions ready at ws://localhost:$PORT$server.subscriptionsPath`)
)

【问题讨论】:

不查看代码...您在网络选项卡中看到了什么?检查地址/端口/协议/标头 在网络选项卡中,我看到了对 graphql 服务器的请求,其中有一个响应数据为空的订阅 没有认证用户?抛出错误而不是只记录?现在即使没有用户,您的代码也会继续 是的,当然有 auth 用户 问题是订阅只在操场上有效,useSubscription 挂钩仅在组件挂载时运行,并且每当我创建新订单时挂钩不会运行 【参考方案1】:

解决订阅中的有效负载


 Subscription: 
        orderNotification: 
            subscribe: (_, __,  pubsub ) => pubsub.asyncIterator(ORDER_NOTIFICATION),
             resolve: (payload) => 
                   return payload;
             ,
        
    

【讨论】:

以上是关于graphql 订阅返回空数据的主要内容,如果未能解决你的问题,请参考以下文章

GraphQL 查询 - 抑制非空错误并返回部分数据

当graphQL的查询从mongodb返回空数据时,你应该检查啥?

Apollo graphql 正在为 mongodb 中的空子文档返回空数据

GraphQL 查询从 JSON 数据源返回 GraphiQL 和 App 中的空对象

与 Apollo GraphQL 订阅返回值反应

带有 RESTful 的 GraphQL 返回空响应