使用 Graphql 在 NestJS 上订阅时,“不能为不可为空的字段返回 null”
Posted
技术标签:
【中文标题】使用 Graphql 在 NestJS 上订阅时,“不能为不可为空的字段返回 null”【英文标题】:"Cannot return null for non-nullable field" when subscribing on NestJS with Graphql 【发布时间】:2019-12-11 15:51:32 【问题描述】:我有一个使用Nestjs
完成的nodejs 后端,我正在使用Graphql
。我的前端是 Ionic/Angular,使用 Apollo-angular 处理 graphql 的东西。
我在订阅数据添加/更改时遇到问题。 Playground(由 Nestjs 提供)运行良好,这提示我问题出在前端。
我的数据模型中有game
和scores
,每个分数都属于一个游戏。在前端,我正在尝试收听添加到特定游戏的新分数。
后端
这是来自我的resolver
的sn-p:
@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');
前端的错误消失了,但是它搞砸了订阅中的数据,游乐场在每个属性中获得了带有 null 的附加分数,并且根本没有触发前端中的订阅。
任何帮助,我在这里做错了什么? 在我看来,我的前端不正确,但我不确定这是我的错误还是 Apollo-angular 中的错误...
【问题讨论】:
score
是否在服务方法中定义(即 create 实际上返回创建的模型)?
是的,服务方法正确返回实际保存的模型(在所有这些情况下)。没有在后端订阅部分定义任何 resolve
,一切都在操场上运行良好,但前端 Apollo 客户端获得 ERROR Error: GraphQL error: Cannot return null for non-nullable field Subscription.scoreAdded.
【参考方案1】:
好的,我的问题解决了。正如我所怀疑的,问题出在前端代码中。所以我在后端实现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',
,
,
;
【讨论】:
你从哪里导入 getMainDefinition ? 立即找到您的问题和答案。我怀疑你至少拯救了我的一天。谢谢。【参考方案2】:上面提供的答案是正确的,但是对于那些想要查看使用的包版本和导入的文件的人来说,请检查以下解决方案:
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
【讨论】:
以上是关于使用 Graphql 在 NestJS 上订阅时,“不能为不可为空的字段返回 null”的主要内容,如果未能解决你的问题,请参考以下文章
错误:使用 Nestjs + Graphql + Typeorm 时无法确定 GraphQL 输入类型
GraphQL Schema 未使用 NestJS 更新(代码优先方法)