Hello from the Dev cave! 🍻
Hope you are safe and healthy.
This one is about how I implemented a notification system in one of my projects using Apollo GraphQL subscriptions.
The example that I am going to use here is an example project that has 2 types of users - manager and associate and below are 2 scenarios.
As a manager and a logged in user,
- I want to get notified when my associate raises a request
- I want my associate - if logged in - to get notified when I raise a request for them.
As an associate and a logged in user,
- I want my manager - if logged in - to get notified when I raise a request.
- I want to get notified when my manager raises a request for me.
Let's see how I have implemented this!
A resolver name should match the subscription name like newRequestAdded here. In that object, we will have a subscribe object where you define the logic for what data should be returned.
I am using withFilter method that assists me to filter out data that needs to be returned. This is optional.
First parameter that withFilter accepts is the function that you would use to register an event. Subscriptions here work on the concept of PubSub or Publish-Subscribe i.e. you define an event and register it.
How we got this pubsub instance here, I will cover that in the points below. Just the thing to consider is that each subscription has the same pubsub instance throughout the app and each subscription resolver must return an asyncIterator with the array of event names to register those events and get in the pubsub ecosystem.
Second parameter is filter functions that returns true if the criteria is met and subscription should be sent to a client. This function can be async as per your requirements. In this function, you will get
- payload which the resolved value via the mutations's resolver.
- variables i.e. the stuff you got as a subscription variable
- context if you choose to inject this while configuring your Apollo server for subscriptions.
The best way to inject the pubsub instance is via context. That way you can have a single copy and avoid weird traps or situations like circular dependencies. So say I added a new request in my DB and before I return it from the resolver I use the publish method on my pubsub instance to emit this event with a payload (sounds familiar? REDUX - TADA! And that is why I used the work ecosystem above! :D).
Whatever payload you are passing, the key should be same as the subscription name - in this case newRequestAdded and newRequest is the object Mongo returned after saving the new request.
We need to modify our server config. The context here also gives you a connection object. What we need to do is check that if we get a connection object, you can use it for securing your app by accessing API KEY headers from this and/or just return it directly for subscription resolvers to handle.
Once that is done, we need to define the endpoint.
We define a path - or endpoint - which we will configure in our client. Just remember that this is a websocket driven function and hence our endpoint protocol changes i.e. in my case it will be - ws://localhost:5000/subscriptions.
This config object also give us 2 functions onConnect which you can use to return connection parameters i.e. the variables received AND onDisconnect.
Once it is done, give it a shot using GraphiQL. That's all the config we need on the server side, rest is all your logic.
Give this a shot and we'll talk about frontend in the next post!