graphql-subscriptions
graphql-subscriptions copied to clipboard
Subscription: make `Subscription` resolver independent of resolvers for `Create` and `Update` records
How do you make a Subscription
resolver independent of resolvers for Creating
and Updating
records
❌ Current behaviour: subscribeTasks
resolver doesn't have proper filters, nor does it return me the latest Task
items
✔️ Expected behaviour: subscribeTasks
resolver returns me all Task
items given a filter parameter (based on TaskWhereInput
) in the query
I'm building a GraphQL API using NestJS.
The API is simple, it has to do CRUD operations on Task
entity - I'm using PostgreSQL
Data model:
import { Field } from '@nestjs/graphql';
import { ObjectType } from '@nestjs/graphql';
import { ID } from '@nestjs/graphql';
@ObjectType()
export class Task {
@Field(() => ID, { nullable: false })
id!: string;
@Field(() => String, { nullable: true })
title!: string | null;
@Field(() => Date, { nullable: true })
createdAt!: Date | null;
@Field(() => Boolean, { nullable: true, defaultValue: false })
completed!: boolean | null;
@Field(() => Date, { nullable: true })
completedAt!: Date | null;
}
The API needs to have the following 3 core resolvers:
-
createTask
=> creates a task in db -
updateTask
=> update a task in db -
subscribeTasks
=> subscribe to tasks given a filter parameter, and the tasks subscribed need to update whenever aTask
is created or updated
⚠️My biggest question:
How do I make sure subscribeTasks
resolver only updates the tasks subscribed BASED ON CHANGES IN THE DB? (rather than triggering updates all the tasks subscribed using PubSub
by publishing pubSubEvents
in createTask
and updateTask
resolvers?)
I also need a proper filter to be applied on subscribeTasks
resolver. So that I can write subscription queries like how I'd do with a normal gql query like this:
subscription {
subscribeTasks (
where: {
completed: {
equals: false
},
createdAt: {
equals: "2022-06-25"
}
}
) {
id
title
completed
completedAt
createdAt
}
}
TaskWhereInput:
@InputType()
export class TaskWhereInput {
@Field(() => [TaskWhereInput], { nullable: true })
AND?: Array<TaskWhereInput>;
@Field(() => [TaskWhereInput], { nullable: true })
OR?: Array<TaskWhereInput>;
@Field(() => [TaskWhereInput], { nullable: true })
NOT?: Array<TaskWhereInput>;
@Field(() => StringFilter, { nullable: true })
id?: StringFilter;
@Field(() => StringNullableFilter, { nullable: true })
title?: StringNullableFilter;
@Field(() => DateTimeNullableFilter, { nullable: true })
createdAt?: DateTimeNullableFilter;
@Field(() => BoolNullableFilter, { nullable: true })
completed?: BoolNullableFilter;
@Field(() => DateTimeNullableFilter, { nullable: true })
completedAt?: DateTimeNullableFilter;
}
This is how my resolvers look like:
import { Resolver, Mutation, Subscription, Args, Query } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import { TaskCreateInput } from 'src/generated/task/task-create.input';
import { TaskUpdateInput } from 'src/generated/task/task-update.input';
import { TaskWhereUniqueInput } from 'src/generated/task/task-where-unique.input';
import { TaskWhereInput } from 'src/generated/task/task-where.input';
import { Task } from 'src/generated/task/task.model';
import { TaskService } from './Task.service';
@Resolver(() => Task)
export class TaskResolver {
constructor(
private readonly taskService: TaskService,
private pubsub: PubSub,
) {}
@Mutation(() => [Task])
public async createTask(@Args('input') input: TaskCreateInput) {
try {
const res = await this.taskService.createTask(input);
this.pubsub.publish('taskUpdatedEvent', { taskUpdatedEvent: res });
return res;
} catch (error) {
throw error;
}
}
@Mutation(() => [Task])
public async updateTask(
@Args('where') where: TaskWhereUniqueInput,
@Args('data') data: TaskUpdateInput,
) {
try {
const res = await this.taskService.updateTask(where, data);
this.pubsub.publish('taskAddedEvent', { taskAddedEvent: res });
return res;
} catch (error) {
throw error;
}
}
@Subscription(() => [Task])
public async subscribeTasks(
@Args('where', { nullable: true }) where?: TaskWhereInput,
) {
this.pubsub.asyncIterator('taskAddedEvent');
this.pubsub.asyncIterator('taskUpdatedEvent');
return await this.taskService.tasks(where);
}
@Query(() => [Task])
async tasks(@Args('where', { nullable: true }) where: TaskWhereInput) {
return await this.taskService.tasks(where);
}
}
Speaking of subscription filters, I also have no idea how to apply what's on NestJS doc in my use case (MANY filters, not just one)
NestJS doc filter demo:
@Subscription(returns => Comment, {
filter: (payload, variables) =>
payload.commentAdded.title === variables.title,
})
commentAdded(@Args('title') title: string) {
return pubSub.asyncIterator('commentAdded');
}
- [ ] has-reproduction
- [ ] feature
- [x] blocking
- [ ] good first issue