How to

How to add In-App notifications to any web app!

Add a notification system to any app in just a few simple steps!

Sumit Saurabh
Sumit SaurabhMay 9, 2023

TL;DR

In this article, you’ll learn how to add a notification system to any app in just a few simple steps! The primary focus in making this app was to showcase just how simple it is to add notifications to an app.

Notifications generator homepage
The text entered is sent as a notification

If you take any modern app, there’s a high chance that it would have notifications functionality baked in. And why not!

Notifications make any app better by providing real-time updates and keeping users engaged. They help increase user retention by providing a medium of communication between an app and its users.

If you’re reading this article, chances are that you already understand the importance of having notifications in your app and are looking for a way to add a notification system to your app.

Well, you’ve come to the right place. In this tutorial, we’ll have a look at how we can add notifications to any app. Now, writing a custom solution every time you want to add notifications somewhere is cumbersome, I know.

But we’ll use a magical tool to help make our jobs easier and believe me, this whole thing will be much simpler than you might think, pinky promise!

How, you ask?

Enter Novu!

Novu helped me solve the biggest problem I had every time I wanted to add notifications to an app – writing a notifications center from scratch.

And even though, I could re-use parts of what I’d written earlier, the unique use cases of each app ensured, I had to make significant changes to my previous system.

With Novu, I could simply grab an API key and use the custom component to add notifications to any web app. PERIOD!

Novu – Open-source notification infrastructure for developers:

Novu is an open-source notification infrastructure for developers. It helps you manage all the product notifications be it an in-app notification (a bell icon like what’s there in Facebook), Emails, SMSs, Discord, and what not.

I’ll be super happy if you check us out on GitHub and give us a star! ❤️
https://github.com/novuhq/novu

Let’s write some code:

We’ll make our app in two stages – backend and front-end. Both will live in separate GitHub repositories and I’ll also show you how to deploy both to the web, enabling us to access the app from anywhere.
Let’s start with the backend.

Basic set-up:

We’ll start with an empty git repo. Create an empty git repository, enter all the relevant details, and publish it to GitHub. Then open it in your IDE (I’m going to use VS Code):

Our first step here would be to install all the required packages. We’ll rely on several packages from npm (Node Package Manager).

To start this process, we’ll generate a package.json using the following command:

1npm init -y

This command generates the package.json file and now we can install all the packages we need. Use the following command to install the packages we’ll be using:

1npm i novu body-parser cors dotenv express mongoose nodemon

Now, create a ‘.env’ file and a ‘.gitignore’ file in the root of the project. We’ll keep sensitive data like our MongoDB connection URL and the Novu API key in the .env file. To stop it from getting staged by git and being pushed onto GitHub, add the .env file to the .gitignore file, as shown below. This way it’ll be accessible to us in the app but nobody will be able to see it on Github.

add novu api key and mongoDB connection URL in .env file and add the .env file to .gitignore

Connecting to a database:

After completing the basic setup, it is now time for us to connect to a database.

If you haven’t already, create your MongoDB account and sign in.

We’ll need to get a connection URL from our database and will plug that into our backend. To get that URL, go to the top left corner and create a new project.

Creating a new project on MongoDB

Give your project a name and then click the ‘build a database’ button:

Building a database in MongoDB

After this, choose the free option and leave everything else to default.

Click the 'build a database' button

Now, enter the username and password that you want to use for database authentication, and make sure that you’ve noted down the password (we’re going to need it).

enter username and password

Now, only the last step is left which is to obtain our connection URL. To get it, click on the ‘connect’ button

Click the connect button to get connection URL

Then choose the ‘compass’ option.

Choose the 'compass' option to get the connection URL

Now, note down the URL (highlighted text in the image):

copy the connection URL and change your password

In this URL, you’ll have to replace ” with your actual password (that you noted down above). That’s it!

Store this URL in a variable in your ‘.env’ file as shown below:

Store the connection URL in '.env' file

Obtaining Novu API key:

Getting the Novu API key is quite simple. You just need to head over to Novu’s web platform

Novu web platform

Then create your account and sign in to it. Then go to ‘Settings’ from the left navigation menu:

Settings tab in the left menu

Now, in the settings, go to the second tab called ‘API Keys’. There, you’ll see your Novu API key. Copy it and add it to the .env file in your project’s root directory:

env file with connection URL and Novu API Key

Let’s start coding:

The backend for this app is rather simple and consists of a few key parts:

  1. Controller: It contains code for the function that runs when we make server requests from our front-end. It just contains one simple function:
1import { inAppNotification } from "../Novu/novu.js";
2import Notif from "../models/notif.js";
3
4export const createNotif = async (req, res) => {
5    const { description } = req.body
6    const newNotif = new Notif({
7        description
8    });
9    try {
10        await newNotif.save();
11        await inAppNotification(description, "Sumit");
12        res.status(201).json(newNotif);
13    } catch (error) {
14        res.status(409).json({ message: error });
15    }
16}

The function starts by destructuring the description property from the req.body object, which is passed in as a parameter when the function is called from the front-end.

Next, a new instance of the ‘Notif’ model is created using the description value, which is then saved to a database using the ‘save()’ method.

After the notification is saved to the database, an in-app notification is sent using the ‘inAppNotification’ function, which is imported from the “../Novu/novu.js” module.

This function takes two arguments:

  • The description of the notification, and
  • The name of the user who triggered the notification, in this case, it is my name: “Sumit”.

If everything is successful, the function sends an HTTP status code of 201 along with the new notification object in the response.

On the contrary, if an error occurs during the process, the function sends an HTTP status code of 409 along with an error message in the response.

Database Schema:

In MongoDB, data is stored in things called ‘Collections’. Collections are like boxes within which we store data. A database in this case is the room. So the database contains entities called ‘collections’ and we store data within those collections.

The schema for notifications is as follows:

1import mongoose from "mongoose";
2
3const notifSchema = mongoose.Schema(
4  {
5    description: { type: String, required: true }
6  },
7  {
8    collection: "notif",
9  }
10);
11
12export default mongoose.model("Notif", notifSchema);

Here, we’re defining a ‘notif’ collection in our database. We’re using the ‘Mongoose’ library to connect to the database.

Using ‘mongoose.Schema’ method, we’re creating a new schema object for the “notif” collection. The object passed as the first parameter is defining the shape of the documents in the collection.

In this case, the schema is defining a single field called “description”, which is of type String and is required (i.e., it must be present in every document).

The second parameter being passed to mongoose.Schema is an optional ‘options’ object. In this case, it is setting the name of the collection to “notif”.

Finally, we’re using ‘mongoose.model’ method to create a model based on the schema. This model is being exported as the default export of this module.

The benefit of exporting it is that we can now use it to interact with the “notif” collection in the MongoDB database.

Now, off to the sweet part!

Setting up Novu notification template:

We’re now left with two tasks in the backend:

  • Configuring Novu, and
  • Starting the server

To configure Novu, go the the web platform again and go to ‘Notifications’ from the left menu bar:

Notifications button on the left meny

Now, create a new workflow, give it a name, and go to the workflow editor:

Workflow editor in Novu

Since we’ll be using just one functionality – In-app notifications, we’ll set only that one up. But with Novu, you can send notifications through pretty much every channel out there, be it email, sms, push, or chat.

To setup the in-app channel, drag the button called ‘in-app’ from the right options menu and click on it to edit it.

Because we’re using ‘description’ in the backend, we’ll have to plug that in here as shown below:

Using 'description' in Novu

Now, back to the code:

In the controller above, we had used a function called inAppNotification but we never defined it. Now is the time to do so:

So, go to your project’s root and create a new file. In that file, we’ll create this function:

1import { Novu } from '@novu/node'; 
2
3
4export const inAppNotification = async (description, Id) => {
5    const novu = new Novu(process.env.NOVU_API_KEY);
6    await novu.subscribers.identify(Id, {
7      firstName: "inAppSubscriber",
8    });
9
10    await novu.trigger("in-app", {
11      to: {
12        subscriberId: "Sumit",
13      },
14      payload: {
15        description: description
16      },
17    });
18  };

Notice the use of ‘description’ here (that we set up in Novu) in the previous step.

Now, the last step of the back-end is remaining, which is setting up the server and the routes.

Our app is quite simple here and we need just one route:

1import express from "express";
2import { createNotif } from '../controller/notif.js'
3
4const router = express.Router();
5
6router.post('/', createNotif)
7
8export default router;

And the server setup is also relatively straight forward:

1import express from "express";
2import mongoose from "mongoose"
3import cors from "cors"
4import bodyParser from "body-parser";
5import dotenv from "dotenv";
6import notifRoute from "./routes/notif.js"
7
8dotenv.config();
9
10const app = express();
11
12app.use(bodyParser.json());
13app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
14app.use(cors());
15
16app.use("/home", notifRoute)
17
18app.listen(3000, function () {
19    console.log('listening on 3000')
20})
21
22app.get('/', (req, res) => {
23    res.send('Project running!')
24})
25
26
27const CONNECTION_URL = process.env.CONNECTION_URL;
28const PORT = process.env.PORT || 8000
29app.use("/", (req,res) => res.send('this is working'))
30mongoose.connect(CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
31    .then(() => app.listen(PORT, () => console.log(`server running on port: ${PORT}`)))
32    .catch((error) => console.log(error));

With these out of the way, our backend is done and we can now move to the front end.

Coding up the front-end:

In the front-end, we’re gonna use the magical power of Web component. It is a collection of three things:

  • NovuProvider,
  • PopoverNotificationCenter, and
  • NotificationBell

These are all combined into one simple component, that we call Web Component. Web component is what enables us to embed this notification center into just about any web app.

You can read more about it here!

Web component docs

Now, in the front-end, we’re gonna need four files:

  • ‘index.html’ – This will contain the markup,
  • ‘styles.css’ – This will contain the CSS styles,
  • ‘app.js’ – This will contain all the logic to send notifications from front-end to back-end and fetch them all in the bell icon, and
  • the ‘.env’ file – This will contain our Novu API key

The contents of each one of them are as follows, starting with ‘index.html’ below:

1<!DOCTYPE html>
2<html lang="en">
3<head>
4    
5    <meta http-equiv="X-UA-Compatible" content="IE=edge">
6    <meta name="viewport" content="width=device-width, initial-scale=1.0">
7    <link rel="stylesheet" href="./styles.css">
8    <script src="https://novu-web-component.netlify.app/index.js"></script>
9    <title>Notification Generator</title>
10</head>
11<body>
12    <div class="header">
13        <h1>Notifications Generator</h1>
14        <notification-center-component style="display: inline-flex" application-identifier="SWMw97ec1ZNA"
15            subscriber-id="Sumit" class="bell"></notification-center-component>
16    </div>
17    <div class="content">
18        <img class="img" src="./notification-bell.png" alt="notification bell image" srcset="">
19        <form id="form" action="#" method="post">
20            <input class="input" type="text" placeholder="Enter notification text and click the send button!" name="description">
21            <button id="btn">Send</button>
22        </form>
23    </div>
24    <footer>
25        <p>Proudly powered by <a class="link" href="http://www.novu.co">Novu</a></p>
26    </footer>
27    <script src="./app.js"></script>
28</body>
29</html>

Then, the ‘app.js’ file is as follows:

1const form = document.querySelector('#form')
2
3
4// extracting send button
5const btn = document.querySelector('#btn');
6const input = document.querySelector('.input')
7
8// btn.onclick = () => console.log('clicked');
9
10form.addEventListener("submit", async (e) => {
11    e.preventDefault();
12    console.log('button clicked');
13    await fetch('https://notificationsgeneratorbackend.onrender.com/home', {
14        method: 'POST',
15        body: JSON.stringify({
16            description: input.value,
17        }),
18        headers: {
19            'Content-Type': 'application/json'
20        }
21    })
22        .then(response => response.json())
23        .then(data => console.log(data))
24        .catch(error => console.error(error));
25        input.value = ""
26
27})
28
29// extracting input value on button click
30
31// here you can attach any callbacks, interact with the web component API
32let nc = document.getElementsByTagName('notification-center-component')[0];
33nc.onLoad = () => console.log('hello world!');

Finally, we’ll have some CSS code for styling in the styles.css file and the Novu API key in the .env file.

You can find the CSS I’ve used here.

If you’ve done everything correctly, you should have an app that looks something like this:

Home page of the app

In this app, we’ve demonstrated how to use the ‘Web component’ to add a notification center to any web app. To keep things simple, we’ve just used the in-app notification feature but you can use just about any notification medium you can imagine – from chat to sms to emails and more!

Deploying our front-end and our back-end:

The last step remaining is to deploy our front-end and our back-end. We’re gonna use a service called ‘Render’ to deploy our back-end and the good, old ‘Netlify’ for our front-end:

Both are very straight forward and the process is a simple plug-and-play kinda thing: Just log in to both, point to your GitHub repo, and add the environment variables as defined in the ‘.env’ file of both. That’s it!

If you want to go over my code, it is available here:

Lastly, if you need help with anything and wanna reach out to me, I’m always available at the Novu discord. Feel free to join in and say hi! 👋

This entire project was made possible by the awesomeness of Novu and it takes me a lot of time and effort to come up with tutorials like this one, so if you could spare a moment and give us a star on our GitHub repo, it would mean a lot to me.

Thanks for all your support! ⭐⭐⭐⭐⭐

Give us a star if you liked this tutorial

Don’t forget to comment what you liked and didn’t like about this tutorial.
Have a great one, bye! 👋

Sumit Saurabh

Sumit Saurabh
Sumit SaurabhMay 9, 2023

Related Posts

How to

Building an Investor List App with Novu and Supabase

Building an Investor List App with Novu and Supabase

Prosper Otemuyiwa
Prosper OtemuyiwaMarch 15, 2024
How to

Implementing Internationalization in Apps: How to Translate Notifications

Implementing Internationalization in Apps: How to Translate Notifications

Prosper Otemuyiwa
Prosper OtemuyiwaMarch 1, 2024
How to

🔥 Building an email automation system with React Flow and Resend 🎉

Creating an email automation system to message people with a sequence of messages every 10 minutes.

Nevo David
Nevo DavidJuly 31, 2023