I have a nodejs backend done with Nestjs
and I'm using Graphql
. My frontend is Ionic/Angular using Apollo-angular for graphql stuff.
I'm having a problem subscribing data additions / changes. Playground (provided by Nestjs) works just fine, which gives me a hint that the problem is in frontend.
I have game
and scores
in my data model, each score belonging to a game. In frontend I'm trying to listen to the new scores added to a specific game.
Here's a snippet from my 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');
}
Here's the service
method:
async createScore(data: any): Promise<IScore> {
const score = await this.scoreModel.create(data);
this.pubSub.publish('scoreAdded', { scoreAdded: score });
}
These are in my schema.gql:
type Score {
id: String
game: String
result: Int
}
type Subscription {
scoreAdded(gameId: String!): Score!
}
Based on Apollo-angular
's documentation, in my frontend I have this kind of service:
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;
}
This is in the frontend's graphql.queries:
export const SCORE_ADDED = gql`
subscription scoreAdded($gameId: String!) {
scoreAdded(gameId: $gameId) {
id
game
result
}
}
`;
and I'm using this service like this in my component:
this.scoreListener.subscribe({ gameId: this.gameId }).subscribe(({ data }) => {
const score = data.scoreAdded;
console.log(score);
});
With all this, my frontend gives me an error ERROR Error: GraphQL error: Cannot return null for non-nullable field Subscription.scoreAdded.
Doing the subscription like this in Playground works, no problem at all.
subscription {
scoreAdded(gameId: "5d24ad2c4cf6d3151ad31e3d") {
id
game
result
}
}
I noticed that if I use resolve
in my backend's resolver like this:
@Subscription(returns => Score, {
resolve: value => value,
filter: (payload, variables) => payload.scoreAdded.game + '' === variables.gameId + '',
})
scoreAdded(@Args('gameId') gameId: string) {
return this.pubSub.asyncIterator('scoreAdded');
}
the error in frontend goes away, BUT it screws up the data in subscription, playground getting the added score with null in each attribute and the subscribe in frontend is NOT triggered at all.
Any help, what am I doing wrong here? It looks to me that my frontend is not correct but I'm not sure is it my bad or possibly a bug in Apollo-angular...
Ok, got my problem solved. As I suspected, the problem was in the frontend side code. So nothing wrong with the way I implemented the nestjs stuff on the backend side. Turned out to be a stupid mistake by me, not initializing the WS for subscriptions, which was clearly explained here https://www.apollographql.com/docs/angular/features/subscriptions/.
So, I changed this
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',
},
},
};
}
to this
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',
},
},
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With