- In GraphQL you can:
- Fetch data using
Query
operation type. - Modify/insert data using
Mutation
operation type.
- Fetch data using
- But if you need to push data from the server to the clients, that choose to listen to real time messages from the server, you need to use
Subscription
operation type. - More complex to implement, so first make sure that you need it.
- Subscription: long-lasting GraphQL read operations that can update their result whenever a particular server-side event occurs.
- Done through the WebSocket protocol.
- They're similar to
query
operation types. - It defines top-level fields that clients can subscribe to:
type Subscription { # messageCreated field will update its value whenever a new Message is # created on the backend, thus pushing the Message to subscribing clients. messageCreated: Message }
- Open a channel in the backend on the server.
- A result is sent to the client every time a particular event happens on the server.
Important
Each subscription
operation can subscribe to only one top-level field of the Subscription
type. Meaning the following subscription operation is not valid:
subscription IndexPageEvents {
notificationCreated {
id
}
postCreated {
author
comment
}
}
- No built-in support for subscriptions.
- Return an object.
- Define a subscribe function.
- Publish an event whenever the return value of a subscription should be updated.
- It can be triggered by:
- A mutation.
- A cron job.
- etc.
- It can be triggered by:
- You can see how it is done in NodeJS + ExpressJS + Apollo server here.
- You need to enable CORS in your backend.
- Do not forget to set up HTTP body parsing
- In NestJS it is by default activated.
- In ExpressJS just
app.use(express.json())
.
- To filter data you can use
withFilter
function.- Runs before the
resolve
function. - Returns early if the filter does NOT pass.
- Accepts two functions:
- The first is your usual subscribe which has to return an
AsyncIterator
object. - The second function though is where you write extra logic to narrow what should be sent to the client based on their query.
- The first is your usual subscribe which has to return an
- Runs before the
- Define a new
Subscription
- This name is either:
- Inherited from the name of the subscription handler method (e.g.,
commentAdded
). - Is provided explicitly by passing an option with the key name as the second argument to the
@Subscription()
decorator.
- Inherited from the name of the subscription handler method (e.g.,
- This name is either:
-
Enable subscription in AS.
- We need a 3rd-party lib for it called
graphql-ws
.
- We need a 3rd-party lib for it called
-
GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: join(__dirname, 'src', 'schema.gql'), sortSchema: true, + subscriptions: { + 'graphql-ws': true, + }, })
[!CAUTION]
You cannot use
GqlOptionsFactory
as your return type. It is a generic type and in our case we wanna useApolloServer
as our driver, but if you use it then you won't be able to access properties such assubscriptions
:And here is how you do should do it instead:
-
Use
@Subscription
to annotate your handler. -
pnpm add graphql-subscriptions
- Provides a simple publish/subscribe API.
- We usually need to back it with an external store such as Redis, or RabbitMQ, or anything other database (See a more complete list here).
-
pnpm add graphql-redis-subscriptions @nestjs/config ioredis class-transformer class-validator
- For docker I am using this
compose
file. -
cd libs/shared && nest g module pubsub
-
-
To publish an event, we use the
pubsub.publish
method.- Often used within a mutation to trigger a client-side update when a part of the object graph has changed.
- BTW it is also possible to use Javascript function generators instead. Like what we did for
greet
subscription.
-
If you need to pass arguments to your field you need to utilize
@ResolveField
.- And if you need to access to the response you can use
@Parent
.
- And if you need to access to the response you can use