Adding Real-Time Notifications to Ghost CMS Using MongoDB and Server-Sent Events
Rate this tutorial
In a nutshell, Change Streams register data flowing into our database. We can subscribe to this data stream and react to it. Reacting means sending out SSE messages to connected clients whenever a new webhook is received and stored.
Its event-based nature matches perfectly with webhooks and SSEs. We can react to newly received webhooks where the data is created, ensuring data integrity over our whole application.
Let's get started by cloning the
1-get-startedbranch from this Github repository:
Make sure to fill out the MONGO_HOST environment variable with your connection string!
Express and the database client are already implemented. So in the following, we'll focus on adding MongoDB change streams and server-sent events.
Once everything is set up, you can start the server on
The application uses two important endpoints which we will extend in the next sections:
/api/notification/subscribe<- Used by EventSource to receive event messages
/api/notification/article/create<- Used as a webhook target by Ghost
Open the cloned project in your favorite code editor. We'll add our SSE logic under
In a nutshell, implementing SSE requires three steps:
- Write out an HTTP status 200 header.
- Write out an opening message.
- Add event-based response message handlers.
We’ll start sending a static message and revisit this module after adding ChangeStreams.
You can also
git checkout 2-add-sseto see the final result.
Writing the HTTP header informs clients of a successful connection. It also propagates the response's content type and makes sure events are not cached.
Add the following code to the function
The first message sent should have an event type of 'open'. It is not mandatory but helps to determine whether the subscription was successful.
Append the following code to the function
We can customize the content and timing of all further messages sent. Let's add a placeholder function that sends messages out every five seconds for now. And while we’re at it, let’s also add a handler to close the client connection:
Append the following code to the function
To check if everything works, visit
src/components/notification/notification.model.tsnext. We'll add a simple
insertcommand for our database into the function
You can also
git checkout 3-webhook-handlerto see the final result.
And on to
src/components/notification/notification.controller.ts. To process incoming webhooks, we'll add a handler function into
This handler will pick data from the incoming webhook and insert a new notification.
So far, we can send SSEs and insert Ghost notifications. Let's put these two features together now.
Earlier, we added a static server message sent every five seconds. Let's revisit
src/components/notification/notification.listener.tsand make it more dynamic.
First, let's get rid of the whole
setIntervaland its callback. Instead, we'll use our
notificationCollectionand its built-in method
watch. This method returns a
You can create a change stream by adding the following code above the
export defaultcode segment:
The stream fires an event whenever its related collection changes. This includes the
insertevent from the previous section.
We can register callback functions for each of these. The event that fires when a document inside the collection changes is 'change':
The variable passed into the callback function is a change stream document. It includes two important information for us:
- The document that's inserted, updated, or deleted.
- The type of operation on the collection.
Let's assign them to one variable each inside the callback:
Let's write the notification to the client. We can do this by repeating the method we used for the opening message.
And that's it! You can test if everything is functional by:
- Opening your browser under
- Using the file under
test/notification.restwith VSCode's HTTP client.
- Checking if your browser includes an opening and a Ghost Notification.
After your Ghost instance is up and running, open your browser at
http://localhost:2368/ghost. You can set up your site however you like, give it a name, enter details, and so on.
In order to create a webhook, you must first create a custom integration. To do so, navigate into your site’s settings and click on the “Integrations” menu point. Click on “Add Webhook,” enter a name, and click on “Create.”
Inside the newly created integration, you can now configure a webhook to point at your application under
* This URL might vary based on your local Ghost setup. For example, if you run Ghost in a container, you can find your machine's local IP using the terminal and
ifconfigon Linux or
And that’s it. Now, whenever a post is published, its contents will be sent to our real-time endpoint. After being inserted into MongoDB, an event message will be sent to all connected clients.
Try this out by starting a local Ghost instance using the provided dockerfile.
You must then instruct your application to serve the JS and CSS assets. Add the following to your
When going back into your Ghost publication, you should now see a small bell icon on the upper right side.
If you’ve followed along, congratulations! You now have a working real-time notification service for your Ghost blog. And if you haven’t, what are you waiting for? Sign up for a free account on MongoDB Atlas and start building. You can use the to get started and explore the full power of MongoDB’s toolkit.