使用 Graphql 订阅 NestJS 时,“无法为不可为空的字段返回 null”

Dvl*_*lpr 5 graphql apollo-client angular nestjs

我已经完成了一个 nodejs 后端Nestjs,我正在使用Graphql. 我的前端是 Ionic/Angular,使用 Apollo-angular 来处理 graphql 的东西。 我在订阅数据添加/更改时遇到问题。Playground(由 Nestjs 提供)工作得很好,这让我暗示问题出在前端。

我有game并且scores在我的数据模型中,每个分数都属于一个游戏。在前端,我试图聆听添加到特定游戏的新分数。

后端

这是我的一个片段resolver

@Mutation(returns => Score)
async addScore(@Args('data') data: ScoreInput): Promise<IScore> {
  return await this.scoresService.createScore(data);
}

@Subscription(returns => Score, {
  filter: (payload, variables) => payload.scoreAdded.game + '' === variables.gameId + '',
})
scoreAdded(@Args('gameId') gameId: string) {
  return this.pubSub.asyncIterator('scoreAdded');
}
Run Code Online (Sandbox Code Playgroud)

这是service方法:

async createScore(data: any): Promise<IScore> {
  const score = await this.scoreModel.create(data);
  this.pubSub.publish('scoreAdded', { scoreAdded: score });
}
Run Code Online (Sandbox Code Playgroud)

这些在我的 schema.gql 中:

type Score {
  id: String
  game: String
  result: Int
}

type Subscription {
  scoreAdded(gameId: String!): Score!
}
Run Code Online (Sandbox Code Playgroud)

前端

根据Apollo-angular的文档,在我的前端,我有这样的服务:

import { Injectable } from '@angular/core';
import { Subscription } from 'apollo-angular';
import { SCORE_ADDED } from './graphql.queries';

@Injectable({
  providedIn: 'root',
})
export class ScoreListenerService extends Subscription {
  document = SCORE_ADDED;
}
Run Code Online (Sandbox Code Playgroud)

这是在前端的 graphql.queries 中:

export const SCORE_ADDED = gql`
  subscription scoreAdded($gameId: String!) {
    scoreAdded(gameId: $gameId) {
      id
      game
      result
    }
  }
`;
Run Code Online (Sandbox Code Playgroud)

我在我的组件中使用这样的服务:

this.scoreListener.subscribe({ gameId: this.gameId }).subscribe(({ data }) => {
  const score = data.scoreAdded;
  console.log(score);
});
Run Code Online (Sandbox Code Playgroud)

问题

有了这一切,我的前端给了我一个错误 ERROR Error: GraphQL error: Cannot return null for non-nullable field Subscription.scoreAdded.

在 Playground 中进行这样的订阅工作,完全没有问题。

subscription {
  scoreAdded(gameId: "5d24ad2c4cf6d3151ad31e3d") {
    id
    game
    result
  }
}
Run Code Online (Sandbox Code Playgroud)

不同的问题

我注意到,如果我resolve像这样在后端的解析器中使用:

  @Subscription(returns => Score, {
    resolve: value => value,
    filter: (payload, variables) => payload.scoreAdded.game + '' === variables.gameId + '',
  })
  scoreAdded(@Args('gameId') gameId: string) {
    return this.pubSub.asyncIterator('scoreAdded');
  }
Run Code Online (Sandbox Code Playgroud)

前端中的错误消失了,但它搞砸了订阅中的数据,操场在每个属性中获得了带有 null 的附加分数,并且根本不会触发前端中的订阅。

任何帮助,我在这里做错了什么? 在我看来,我的前端不正确,但我不确定这是我的错误还是可能是 Apollo-angular 中的错误...

Dvl*_*lpr 5

好的,我的问题解决了。正如我怀疑的那样,问题出在前端代码中。所以我在后端实现 Nestjs 的方式没有任何问题。事实证明这是我的一个愚蠢的错误,没有初始化订阅的 WS,这在此处得到了明确的解释https://www.apollographql.com/docs/angular/features/subscriptions/

所以,我改变了这个

const graphqlUri = 'http://localhost:3000/graphql';

export function createApollo(httpLink: HttpLink) {
  return {
    link: httpLink.create({ graphqlUri }),
    cache: new InMemoryCache(),
    defaultOptions: {
      query: {
        fetchPolicy: 'network-only',
        errorPolicy: 'all',
      },
    },
  };
}
Run Code Online (Sandbox Code Playgroud)

对此

const graphqlUri = 'http://localhost:3000/graphql';
const wsUrl = 'ws://localhost:3000/graphql';

export function createApollo(httpLink: HttpLink) {
  const link = split(
    // split based on operation type
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    new WebSocketLink({
      uri: wsUrl,
      options: {
        reconnect: true,
      },
    }),
    httpLink.create({
      uri: graphqlUri,
    })
  );
  return {
    link,
    cache: new InMemoryCache(),
    defaultOptions: {
      query: {
        fetchPolicy: 'network-only',
        errorPolicy: 'all',
      },
    },
  };
}
Run Code Online (Sandbox Code Playgroud)