• Blog>
  • Knowledge>
  • Integration of a Notification-System with Novu: Is it really worth it?

Integration of a Notification-System with Novu: Is it really worth it?

Lars wuethrich portrait 2024 04 02 155534 mkvs
Lars wuethrich portrait 2024 04 02 155534 mkvs
Lars WüthrichSoftware Engineer

Many applications need to notify users about specific events. This aspect is crucial for enhancing user experience, as notifications, whether shown in an inbox or delivered through various channels, make events visible and transparent, providing a clear record of significant changes within an application. Additionally, notifications can serve as a to-do list, prompting users to complete necessary actions.

Implementing a notification system raises numerous questions about its execution. What channels should be utilized? Typically, in-app notifications, emails, and mobile push notifications are suitable, but sometimes SMS notifications or integration with other external systems may be required. You will also need to identify which events are critical and which not in order to ensure that users aren't overwhelmed by excessive alerts. Otherwise, the feature's value diminishes, and users may start to disregard all notifications. Moreover, during periods of frequent events, it's important to aggregate these into a single notification that conveys all essential information.

Is it possible to use a library that addresses most or all of these challenges? Without a library, you would need to implement:

  • API integration for each communication channel,
  • Customized templating for the notification message for each channel,
  • Message aggregation, also known as message digest, to group similar notifications sent within a short timeframe to avoid spamming users,
  • A data model for your notifications and an internal API for create, read, update, delete (CRUD) operations, as well as actions to mark notifications as read or unread,
  • An in-app notification inbox in your frontend for displaying them,
  • Redirect links from a notification back into your application

We are developing Caturix, a project management software tailored for the construction industry. Caturix addresses several challenges faced by construction companies, such as project management, shift and resource planning, and much more. Therefore keeping people up to date is very important and notifications are a good way to do so.

Inbox
Inbox

To keep users informed when certain events occur that require attention, we decided to implement a notification system. Notifications are sent through multiple channels like email, push, SMS, and in-app, and also function as to-do items in the web or app mailbox.

Many software ticketing systems have a notification system that alerts you in an inbox when something happens, like being assigned to tasks, changes in tasks, and similar events. Our system is a kind of dual system, notifying you of changes across multiple external channels and allowing you to take action. It also serves as a reminder for tasks that need to be completed within the application inbox. This to-do item approach is what we aimed for.

We chose Novu as our notification infrastructure provider. As we are still developing a Minimum Viable Product (MVP), moving quickly and seeking product-market fit, Novu seemed to align well with our start-up constraints. Regarding costs, the business license, including 60,000 events per month, is around $200 per month, which is reasonable for our scale. We won't reach this event count at the moment, but the business license is necessary for internationalization, an important criterion as we operate in a multilingual country and support English, German, French, and Italian. It would have been preferable if internationalization were included in the lower pricing tier. However, the translation support is currently in early-access and still under active development. It doesn't fit our need for the moment so we can stay within the lower tier but we will need to assess this feature again later when it has matured a bit.

Novu allows connecting email, SMS, chat, and push providers. For in-app integration, their client SDK or server API is used.

There are other notification infrastructure systems with similar features and more maturity, but Novu fitted our criteria well enough, and some existing solutions have a higher annual cost. Further, Novu is open source and we could host it ourself, if we wanted to.

Our main goal currently is to find product-market fit, and as requirements for this feature are not completely fixed yet, we will evolve with customer feedback. Therefore, we decided to build the notification sending and inbox based on Novu as it fits well enough.

First, we will examine a few high-level overviews of different parts of Novu, then discuss the technical challenges we faced while integrating Novu, as there were quite a few.

Key Features of Novu

There are three main features that Novu provides which are useful and were considered in its choice apart from the competitive pricing.

  • Adding Providers with Ease: Novu supports a variety of providers for each channel type, simplifying the process with straightforward API key and configuration entry.
  • Message Digest: Sending notifications via API is easy to do with any backend but Novu provides a feature called Message Digest which is quite handy. You can specify a digest period and all messages that are sent within this period are aggregated into a single message. By aggregating messages you prevent a notification spam to the user.
  • Client SDK and webhook: We had to bypass Novu's standard message component and opt for the headless Javascript package, which allowed us to create a custom component better suited to our needs. But for very simple use cases the provided component might be enough.

Pricing

The pricing is based on number of events per month and seems quite competitive compared to other notification services. If the translation support had been better we would have gone with the 'Business' license but this feature is currently still in active development. However, this meant that we had to to some of the templating and translation handling ourselves diminishing the usefulness of Novu in this regard.

Pricing novu
Pricing novu

Novu's Server-Side Sdks

Novu offers both server-side SDKs for managing subscribers, their preferences and triggering notifications from the backend as well as client-side SDK's that interface with their webhook and prebuilt frontend components that show the notifications, allow user's to change their preferences.

At the time of writing Novu has server-side SDKs for:

Technology

NodePhpLaravelPythonKotlinJavaRubyGoC#

For a more detailed view into these server-side SDK's, you can visit Novu's SDK documentation For more information on the API, refer to Novu API-Reference.

The advantage of having these SDKs is significant. They eliminate the need for manual creation of data transfer objects, both for the arguments sent and the data received from the API. This reduces the potential for errors that are commonly encountered when crafting REST requests by hand.

While some services supplement their offerings with Postman or Insomnia collections for API testing, Novu currently does not provide any collection for them.

Our experience with the Kotlin SDK was mostly positive, although it's important to note some serious limitations. The SDK does not appear to be fully updated with the latest API changes. This shortfall results in missing fields, forcing us to occasionally revert to writing REST requests manually. Furthermore, a number of arguments within the SDK are either untyped or are simple strings, which requires us to deduce the necessary arguments based on the REST API endpoints. There exists a big problem with their API as some functionality is missing that would be required if you want to create your custom notification component in the frontend.

One significant challenge with Novu's API is the absence of certain functionalities that are crucial for developing custom notification components in the frontend. For instance, the API endpoint for retrieving messages (Get Messages) lacks the capability to filter notifications based on their 'read' or 'unread' status, among other parameters. Additionally, the API supports the deletion of only individual notifications and not bulk or all notifications for a user, which can be a constraint in certain scenarios.

It appears that Novu’s development efforts have been more focused on the client components they offer. As a result, some functionalities are exclusively available in their client API and are not documented. This emphasis on the client SDK is also evident in the server API, which exhibits limited functionality for certain tasks.

To circumvent these limitations, we resorted to using the undocumented client API on the server side. This approach, discovered through the source code of their headless JavaScript SDK, offers more functionality than what is described in the official API reference. While this method works, as the client SDK is stable and the component packages depend on it, it’s not an ideal solution.

In summary, while Novu's custom components and client SDK are generally sufficient for straightforward notification trigger requests, there are limitations if the custom client SDK for a specific programming language is not available. It's evident that Novu's server API could benefit from further development and alignment with their client SDKs’ capabilities.

Novu's Client-Side SDKs

There exist prebuilt components that are provided for:

Technology

Component

VueprebuildAngularprebuildReactprebuildFlutternot availableWeb-componentprebuildIframe embeddedprebuildHeadlesscustom
Novu component
Novu component

While these prebuilt components might be suitable for some applications and offer a degree of customization through restyling, they may not entirely align with specific needs. In our case, the requirement for a distinct component for our inbox of to-do items and notifications led us to explore beyond the prebuilt options. Given their inherent limitations, you might also find yourself in a position where creating a custom component becomes necessary.

For such custom development you can use the headless npm package which allows to create a websocket connection to novu and offers following features:

  • Listen for changes in unread or unseen notifications
  • Fetch notifications (pagination based, this is flawed and i will cover this later)
  • Allows to mark messages as read, allows to update user preferences
  • Allows to delete all notifications or a single notification

Despite encountering some challenges with notification fetching pagination, the headless service from Novu can be used effectively to create your own custom component.

Novu's Web UI

There exists an API for configuring workflows. Each notification type will have a workflow associated that has channels configured (email, sms, ...) and each channel will have a template into which you can insert certain parameters such as the user name or other data. The templating engine is documented here: Workflow Editor and here: Handlebars & Helpers. Handlebar.js is used for inserting data into the templates.

The problem with this approach is that it basically goes against the "Infrastructure as Code" philosophy. If your configuration is spread over multiple ui screens you have less transparency and accountability over your changes. They offer an API for creating some of these objects but it is nearly undocumented and therefore difficult to use.

Upside is that you can change message templates or configure channels without needing a redeploy of your backend which is quite handy. You can also turn off certain notifications and channels within the ui without redeployment dynamically.

Environments

Currently, Novu only supports two environments. The development environment for testing and the actual production environment. If you have more stages than that you will need to share the development environment, which might make testing harder as notifications may have come from any of these.

Furthermore, Novu does not allow for distinct configurations between the development and production environments. While this design choice ensures consistency between these two stages, you are severely limited if you need to configure something differently for the development environment as you can't do this anymore. To implement changes in the production environment, they must first be tested and then promoted from the development environment. This promotion process ensures that only verified changes are deployed in the production setting.

Documentation

The documentation of Novu is available here: Documentation

Regarding the core objects of their service they have a good explanation. However, their API is not documented well enough in many use cases.

Translation

Novu has rolled out translation support, but as of February 2024, it is still under active development. This feature appears to be in a very early access phase and, in my view, is not seamlessly integrated with the templating system. Without translation, you can define a template for each channel that incorporates specific data, in addition to utilizing handlebar templating language for compiling messages. When incorporating translations, you are required to provide i18n translation files. However, this necessitates the use of a different syntax (custom handlebar helpers) to embed the translations into the message. Moreover, there's no autocomplete for your translation keys, and the syntax required to integrate translations has not been documented yet. This was discovered through discussions with the developers on Discord. I suspect that we are among the first testers of this feature.

Given that the current solution did not meet our needs, we opted to use our own templating language in the backend for the time being, which has its drawbacks and diminishes the utility of Novu slightly. If you have numerous channels that require translated messages, I would advise against using Novu for the time being, at least until their implementation has become more refined.

Security

In terms of security, communication with the Novu API is managed through the use of an application identifier unique to each environment ('development' and 'production'), along with an API key, which can be regenerated through their web ui.

Furthermore, Novu provides a frontend component that displays notifications on the client side. This setup necessitates a security mechanism to ensure that users only access their own notifications. Clients can establish a WebSocket connection using the application identifier (not the API key) and a subscriber ID. To enhance security, Novu allows the usage of an HMAC hash as a requirement. This requires clients to pass a HMAC-signed hash of the subscriber ID, along with the ID itself, to establish the WebSocket connection.

However, this hash is static and depends only on the API secret. If it were to be leaked you would need to regenerate the API key in order to prevent someone from accessing another user's notifications.

Integrating Novu vs writing it yourself

Addressing the question of whether to develop in-house or leverage existing solutions, such as Novu's API for notifications, is always challenging. Technical hurdles are often underestimated by software developers. Therefore, I approach this analysis cautiously, aware that some technical challenges might not be evident until one embarks on the development process. So take this with a grain of salt.

  • Data Model and API for Notifications: This aspect seems to have fewer pitfalls. It involves creating a notification within the database and managing all CRUD operations, including 'mark as read' and 'mark as unread'. However, the integration with Novu was less straightforward than anticipated. Their API's limitations and the absence of certain functionalities in either their backend or client-side API led to complications. A self-developed solution would have potentially offered a smoother integration with our Flutter app, especially since Novu lacks a Flutter/Dart SDK.
  • Channel API Integration: This step appears relatively straightforward, entailing the creation of API requests to send out notifications. While Novu provided some efficiencies, switching providers or making substantial changes could necessitate significant alterations. However, the time saved in this area may not be critical when selecting a notification service provider.
  • Frontend Component: The default component supplied did not meet our specific needs, so no time could be saved as we had to create our own frontend component for displaying notifications.
  • Message Digest and Templating: This area likely poses the most technical challenges. Managing notifications, especially during bursts of activity, requires monitoring, aggregating, and sending notifications only after a certain period of inactivity. Aggregating messages and doing custom templating for these notifications would likely use up a significant part of development time. In addition, additional technical problems might turn up in this case. The only benefit we would have had implementing this our own is that the current amount of notifications sent out is currently quite low so scalability at the moment would not be an important issue for implementation.
  • UI for Configuring Workflows: Developing a user interface to manage all notifications and workflow modifications could be time-consuming. This is the part that would probably need the most time if we wanted to have a UI for all our notifications and do changes to workflows within them. But regarding that I feel that this feature would not have been necessary for our use case. However, doing some changes without redeploying has actually been very useful adding new events requires backend changes as well and having configuration within the code would also follow the 'Infrastructure As Code' philosophy which also would have been beneficial.

In conclusion, while leveraging Novu's API offered some time savings, particularly with their message digest feature and the ease of configuring channel providers, the challenges encountered due to limitations and peculiarities in their API implementation would make the custom implementation more feasible in retrospective. As we also had to write some backend endpoints for our Flutter app, among other issues, indicates that the overall time and effort savings might not have been as significant as anticipated.

Pro

Con

Effective Message Digest featureTranslation support is not ready yet in the current formWide range of providers for each channel, with straightforward configurationThe API could be better designed and more comprehensiveAvailability of Server SDKsLack of feature parity between the server API and client SDK endpointsCompetitive pricingMissing Flutter/Dart SDKServer SDKs, particularly for Kotlin, are not completely up to date and lack typingDeviation from the "Infrastructure as Code" philosophyAwkward separation between 'Development' and 'Production' environment which cannot be configured differently

Summary

In essence, Novu offers a functional service at a competitive price point. If users are comfortable with a UI-centric configuration approach, it proves to be a valuable tool. However, the API's design and some unresolved issues are notable drawbacks. Despite this, the ease of configuring multiple providers for each channel is a time-saving feature.

The big drawback has been the translation support. It does not seem to be well integrated with their templating and message digest engine. Also the feature is very new and some problems still exist as of February 2024. If you need to manage many event types with translations I would strongly recommend against choosing Novu in this case.

Subscribe for exclusive insights into software development

No spam and no blah-blah. Unsubscribe at any time.