在apollo-client / apollo-server和useSubscription中获取的数据仅为null

时间:2020-06-06 10:19:54

标签: apollo publish-subscribe apollo-client apollo-server

我尝试在apollo服务器和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

// subscription.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)

-服务器- onPutUnit.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
    },
  },
}

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

1 个答案:

答案 0 :(得分:1)

您仅为subscribe指定了onPutUnit函数,而没有指定resolve函数。这意味着该字段使用默认的解析器。

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

{
  user {
    name
  }
}

user返回的解析器将是提供给name的解析器的父值(如果user返回Promise,则是Promise解析的内容)。

但是user呢?它没有父字段,因为它是根字段。在这种情况下,将user传递给您在初始化ApolloServer时设置的rootValue(如果没有初始化,则传递{})。

对于订阅,此方法的工作原理有所不同,因为无论您将publish的任何值实际上都作为根值传递给解析器。这意味着您可以通过发布具有与字段名称匹配的属性的对象来利用默认解析器:

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

如果您这样做,则需要提供一个resolve函数来转换您发布的有效负载。例如,如果我们这样做:

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

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

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