代码之家  ›  专栏  ›  技术社区  ›  곽대용

在apollo客户端/apollo服务器中获取的数据仅为null,并使用Subscription

  •  0
  • 곽대용  · 技术社区  · 4 年前

    我尝试在apollo服务器中使用pubsub;阿波罗客户。但订阅的数据仅为空。

    客户端依赖性

     "@apollo/react-hooks": "^3.1.5",
     "apollo-boost": "^0.4.9",
     "apollo-link-ws": "^1.0.20",
     "graphql": "^15.0.0",
     "react": "^16.13.1",
     "react-dom": "^16.13.1",
     "react-router-dom": "^5.2.0",
     "react-scripts": "3.4.1",
     "styled-components": "^5.1.1",
     "subscriptions-transport-ws": "^0.9.16",
     "typescript": "~3.7.2"
    

    服务器依赖性

      "apollo-server": "^2.14.1",
      "graphql": "^15.0.0",
      "merge-graphql-schemas": "^1.7.8",
      "ts-node": "^8.10.2",
      "tsconfig-paths": "^3.9.0",
      "typescript": "^3.9.3"
    

    //apolloClient.ts

    import { ApolloClient, HttpLink, InMemoryCache, split } from 'apollo-boost'
    
    import { WebSocketLink } from 'apollo-link-ws'
    import { getMainDefinition } from 'apollo-utilities'
    
    const wsLink = new WebSocketLink({
      uri: 'ws://localhost:4000/graphql',
      options: {
        reconnect: true
      }
    })
    
    const httpLink = new HttpLink({
      uri: 'http://localhost:4000'
    })
    
    const link = split(
      // split based on operation type
      ({ query }) => {
        const definition = getMainDefinition(query);
        return (
          definition.kind === 'OperationDefinition' &&
          definition.operation === 'subscription'
        );
      },
      wsLink,
      httpLink,
    )
    
    const cache = new InMemoryCache()
    const client = new ApolloClient({
      cache: cache,
      link: link,
    })
    
    export default client
    

    //subscribe.ts

    const ON_PUT_UNIT = gql`
      subscription onPutUnit($code: String!) {
        onPutUnit(code: $code)
      }
    `
    const onPutResult = useSubscription(
        ON_PUT_UNIT,
        { variables: {
          code: code,
        }}
      )
    
    // in is only null!!
    console.log('subscribe', onPutResult)
    

    -服务器- onOutUnit.ts

    type Subscription {
      onPutUnit(code: String!): Room
    }
    
    import { pubsub } from '@src/index'
    const { withFilter } = require('apollo-server')
    export default {
      Subscription: {
        onPutUnit: {
          subscribe: withFilter(
           () => pubsub.asyncIterator(['PUT_UNIT']),
           (payload: any, variables: any) => {
             // no problem in payload & variable data 
             return payload.code === variables.code
           }
          ) 
        }
      },
    }
    
    

    putUnit.ts

    type Mutation {
      putUnit(code: String!, x: Int!, y: Int!, userName: String!): Room!
    }
    
    export default {
      Mutation: {
        putUnit: async (_: any, args: args) => {
          const { code, x, y, userName } = args
          const room = findRoom(code)
          console.log(room) // no problem. normal data.
          pubsub.publish('PUT_UNIT', room)
          return room
        },
      },
    }
    

    有什么问题吗?订阅事件通常在发布时到达客户端。但数据仅为空。我无法解释原因。

    0 回复  |  直到 4 年前
        1
  •  1
  •   Daniel Rearden    4 年前

    您只指定了 subscribe 功能为 onPutUnit ,但未指定 resolve 功能。这意味着该字段使用默认解析器。

    默认解析器只会查找与父对象上的字段同名的属性(传递给解析器的第一个参数),并返回该属性。如果父对象上没有与字段同名的属性,则字段解析为null。父对象是父字段解析为的值。例如,如果我们有这样的查询:

    {
      user {
        name
      }
    }
    

    无论解析器用于什么 user 返回值将是提供给解析器的父值 name (如果 用户 返回一个Promise,它是Promise所决定的)。

    但是呢 用户 ?它没有父字段,因为它是根字段。在这种情况下, 用户 已通过 rootValue 在初始化ApolloServer时设置(或 {} 如果你没有)。

    对于订阅,这有点不同,因为无论你重视什么 publish 实际上作为根值传递给解析器。这意味着您可以通过发布具有与字段名匹配的属性的对象来利用默认解析器:

    pubsub.publish('PUT_UNIT', { onPutUnit: ... })
    

    如果你 不要 不过,要做到这一点,你需要提供 决定 转换您发布的有效负载的函数。例如,如果我们这样做:

    pubsub.publish('PUT_UNIT', 'FOOBAR')
    

    然后我们的解析器映射需要看起来像这样:

    const resolvers = {
      Subscription: {
        onPutUnit: {
          subscribe: ...,
          resolve: (root) => {
            console.log(root) // 'FOOBAR'
            // return whatever you want onPutUnit to resolve to
          }
        }
      },
    }