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

时间:2019-08-03 07:19:27

标签: angular graphql apollo-client nestjs

我有一个用Nestjs完成的nodejs后端,我正在使用Graphql。我的前端是将Apollo-angular用于graphql的东西的Ionic / Angular。 我在订阅数据添加/更改时遇到问题。 Playground(由Nestjs提供)运行正常,这提示我问题出在前端。

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

后端

这是我的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');
}

这是service方法:

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

这些在我的schema.gql中:

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

type Subscription {
  scoreAdded(gameId: String!): Score!
}

前端

根据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;
}

这在前端的graphql.queries中:

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

并且我正在组件中使用这种服务:

this.scoreListener.subscribe({ gameId: this.gameId }).subscribe(({ data }) => {
  const score = data.scoreAdded;
  console.log(score);
});

问题

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

在Playground中像这样进行订阅是可行的,完全没问题。

subscription {
  scoreAdded(gameId: "5d24ad2c4cf6d3151ad31e3d") {
    id
    game
    result
  }
}

不同的问题

我注意到,如果我在后端的解析器中使用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');
  }

前端的错误消失了,但是它搞砸了订阅中的数据,操场上每个属性获得的得分都为空,前端中的订阅完全没有触发。

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

2 个答案:

答案 0 :(得分:0)

好,我的问题解决了。正如我所怀疑的那样,问题出在前端侧代码中。因此,我在后端侧实现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',
      },
    },
  };
}

对此

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',
      },
    },
  };
}

答案 1 :(得分:0)

上面提供的答案是正确的,但是对于那些想要查看所使用的软件包版本和导入的文件的人,请检查此解决方案:

package.json 依赖项

{
   "dependencies": {
      "@apollo/client": "^3.2.5",
      "@apollo/link-ws": "^2.0.0-beta.3",
      "apollo-angular": "^2.0.4",
      "subscriptions-transport-ws": "^0.9.18",
    }
}

graphql.module.ts 代码

import { WebSocketLink } from '@apollo/link-ws';
import { NgModule } from '@angular/core';
import { APOLLO_OPTIONS } from 'apollo-angular';
import { InMemoryCache, split } from '@apollo/client/core';
import { getMainDefinition } from '@apollo/client/utilities';
import { HttpLink } from 'apollo-angular/http';

const uri = 'http://localhost:3000/graphql';
const wsUrl = 'http://localhost:3000/graphql';

export function createApollo(hLink: HttpLink) {
    
    const ws = new WebSocketLink({
       uri: wsUrl,
       options: {
           reconnect: true
       }
    });

    const http = hLink.create({uri});

    const newLink = split(
       ({ query }) => {
          const def = getMainDefinition(query);
          return def.kind === 'OperationDefinition' && def.operation === 'subscription';
       },
       ws,
       http
    );
    
    return {
        link: newLink,
        cache: new InMemoryCache(),
        defaultOptions: {
           watchQuery: {
              fetchPolicy: 'network-only',
              errorPolicy: 'all'
           }
        }
    };
}

@NgModule({
   providers: [
     {
        provide: APOLLO_OPTIONS,
        useFactory: createApollo,
        deps: [HttpLink],
     },
   ],
})
export class GraphQLModule {}