Graphql Subscriptions
Author : JaNakh Pon , September 04, 2021
Tags
Summary
Subscriptions allow clients to listen to real-time messages from the server. The client connects to the server with a bi-directional communication channel using the WebSocket protocol and sends a subscription query that specifies which event it is interested in. When an event is triggered, the server executes the stored GraphQL query, and the result is sent back to the client using the same communication channel.
The client can unsubscribe by sending a message to the server. The server can also unsubscribe at any time due to errors or timeouts. A significant difference between queries or mutations and subscriptions is that subscriptions are stateful and require maintaining the GraphQL document, variables, and context over the lifetime of the subscription.
Graphql Subscriptions with Nest.js
We are going to add subscription to repo from the previous article in a few steps:
- Installation and setup
- Defining methods in resolver/schema for subscriptions
Installation & Schema
Firstly, let's install the required dependency:
> npm i graphql-subscriptions --save
And add GraphQLModule.forRoot({ installSubscriptionHandlers: true,})
to "app.module.ts"
Now, Let's get started by adding subscriptions to "resolver":
import { Query, Resolver, Args, ID, Mutation, Subscription } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import { RowCount, SearchResponse, SortType, Status, Todo } from '../interfaces/todo.interface'
import { CreateInput, SearchInput, TodoID, TodoObject } from '../types/todo.gql'
import { TodoService } from '../todo.service';
import { UseGuards } from '@nestjs/common';
import { GraphqlJwtAuthGuard } from 'src/user/auth/guard/gql.guard';
import { GqlCurrentUser } from 'src/user/auth/guard/gql.user';
import { User } from 'src/user/interfaces/user.interface';
@Resolver('Todo')
export class TodoResolver {
private pubSub: PubSub;
constructor(private todoService: TodoService) {
this.pubSub = new PubSub();
}
@Query()
@UseGuards(GraphqlJwtAuthGuard)
async me(@GqlCurrentUser() user: User,): Promise<User> {
return user
}
@Query()
async list(
@Args('page') page = 1,
@Args('take') take = 5,
@Args('sort') sort = "updated_at",
@Args('order') order = SortType.D
): Promise<Todo[]> {
return await this.todoService.findAll(page, take, sort, order);
}
@Query()
async searchList(@Args('input') input: SearchInput): Promise<SearchResponse> {
const { page, take, title, text } = input
return await this.todoService.search(page, take, title, text)
}
@Query()
async todo(@Args('id', { type: () => ID }) id: number): Promise<Todo> {
return await this.todoService.findOne(id);
}
@Query()
@UseGuards(GraphqlJwtAuthGuard)
async count(@GqlCurrentUser() user: any,): Promise<RowCount> {
console.log("user", user)
return await this.todoService.getCount()
}
@Mutation()
async create(@Args('input') input: CreateInput): Promise<Todo> {
const todo = await this.todoService.create(input)
await this.pubSub.publish('todoCreated', { todoCreated: todo })
return todo
}
@Mutation()
async update(@Args('id', { type: () => ID }) id: number, @Args('input') input: CreateInput): Promise<Todo> {
const resp = await this.todoService.update(id, input)
await this.pubSub.publish('todoUpdated', { todoUpdated: resp })
return resp
}
@Mutation()
async delete(@Args('id', { type: () => ID }) id: number): Promise<Status> {
const resp = await this.todoService.delete(id);
await this.pubSub.publish('todoDeleted', { todoDeleted: id })
return resp
}
@Subscription(() => TodoObject, {
name: 'todoCreated',
})
todoCreated(): AsyncIterator<Todo> {
return this.pubSub.asyncIterator<Todo>('todoCreated');
}
@Subscription(() => TodoObject, {
name: 'todoUpdated',
})
todoUpdated(): AsyncIterator<Todo> {
return this.pubSub.asyncIterator<Todo>('todoUpdated');
}
@Subscription(() => TodoID, {
name: 'todoDeleted',
})
todoDeleted(): AsyncIterator<Todo> {
return this.pubSub.asyncIterator<Todo>('todoDeleted');
}
}
And "schema" in "todo.graphql":
type Todo {
id: ID!
title: String
text: String
completed: Boolean
}
type SearchResponse {
data: [Todo]
count: Int
}
type Status {
message: String
}
type User {
userId: Int
email: String
}
type RowCount {
count: Int
}
enum SortType {
ASC,
DESC
}
input SearchInput {
page: Int
take: Int
title: String
text: String
}
input CreateInput {
title: String
text: String
completed: Boolean
}
type Query {
list(page: Int, take: Int, sort: String, order: SortType): [Todo]
searchList(input: SearchInput): SearchResponse
todo(id: ID!): Todo!
me: User
count: RowCount
}
type Mutation {
create(input: CreateInput!): Todo
update(id: ID!, input: CreateInput!):Todo
delete(id: ID!): Status
}
type Subscription {
todoCreated: Todo
todoUpdated: Todo
todoDeleted: ID
}
Now, we can go to localhost:3001/graphql
and play with subscriptions in Graphql Playground! 😉.