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

Sumit Saurabh @sumitsaurabh927

About: Full stack React + Community = ❤️ | Code by the day 🧑🏽‍💻 | content by the night 📝

Location:
Bengaluru, India
Joined:
Mar 15, 2020

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

Publish Date: May 9 '23
183 33

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.

Novu makes adding notifications a breeze

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:

npm init -y
Enter fullscreen mode Exit fullscreen mode

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:

npm i novu body-parser cors dotenv express mongoose nodemon
Enter fullscreen mode Exit fullscreen mode

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:
import { inAppNotification } from "../Novu/novu.js";
import Notif from "../models/notif.js";

export const createNotif = async (req, res) => {
    const { description } = req.body
    const newNotif = new Notif({
        description
    });
    try {
        await newNotif.save();
        await inAppNotification(description, "Sumit");
        res.status(201).json(newNotif);
    } catch (error) {
        res.status(409).json({ message: error });
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

import mongoose from "mongoose";

const notifSchema = mongoose.Schema(
  {
    description: { type: String, required: true }
  },
  {
    collection: "notif",
  }
);

export default mongoose.model("Notif", notifSchema);
Enter fullscreen mode Exit fullscreen mode

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:

import { Novu } from '@novu/node'; 


export const inAppNotification = async (description, Id) => {
    const novu = new Novu(process.env.NOVU_API_KEY);
    await novu.subscribers.identify(Id, {
      firstName: "inAppSubscriber",
    });

    await novu.trigger("in-app", {
      to: {
        subscriberId: "Sumit",
      },
      payload: {
        description: description
      },
    });
  };
Enter fullscreen mode Exit fullscreen mode

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:

import express from "express";
import { createNotif } from '../controller/notif.js'

const router = express.Router();

router.post('/', createNotif)

export default router;
Enter fullscreen mode Exit fullscreen mode

And the server setup is also relatively straight forward:

import express from "express";
import mongoose from "mongoose"
import cors from "cors"
import bodyParser from "body-parser";
import dotenv from "dotenv";
import notifRoute from "./routes/notif.js"

dotenv.config();

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
app.use(cors());

app.use("/home", notifRoute)

app.listen(3000, function () {
    console.log('listening on 3000')
})

app.get('/', (req, res) => {
    res.send('Project running!')
})


const CONNECTION_URL = process.env.CONNECTION_URL;
const PORT = process.env.PORT || 8000
app.use("/", (req,res) => res.send('this is working'))
mongoose.connect(CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => app.listen(PORT, () => console.log(`server running on port: ${PORT}`)))
    .catch((error) => console.log(error));
Enter fullscreen mode Exit fullscreen mode

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:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./styles.css">
    <script src="https://novu-web-component.netlify.app/index.js"></script>
    <title>Notification Generator</title>
</head>
<body>
    <div class="header">
        <h1>Notifications Generator</h1>
        <notification-center-component style="display: inline-flex" application-identifier="SWMw97ec1ZNA"
            subscriber-id="Sumit" class="bell"></notification-center-component>
    </div>
    <div class="content">
        <img class="img" src="./notification-bell.png" alt="notification bell image" srcset="">
        <form id="form" action="#" method="post">
            <input class="input" type="text" placeholder="Enter notification text and click the send button!" name="description">
            <button id="btn">Send</button>
        </form>
    </div>
    <footer>
        <p>Proudly powered by <a class="link" href="http://www.novu.co">Novu</a></p>
    </footer>
    <script src="./app.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Then, the 'app.js' file is as follows:

const form = document.querySelector('#form')


// extracting send button
const btn = document.querySelector('#btn');
const input = document.querySelector('.input')

// btn.onclick = () => console.log('clicked');

form.addEventListener("submit", async (e) => {
    e.preventDefault();
    console.log('button clicked');
    await fetch('https://notificationsgeneratorbackend.onrender.com/home', {
        method: 'POST',
        body: JSON.stringify({
            description: input.value,
        }),
        headers: {
            'Content-Type': 'application/json'
        }
    })
        .then(response => response.json())
        .then(data => console.log(data))
        .catch(error => console.error(error));
        input.value = ""

})

// extracting input value on button click

// here you can attach any callbacks, interact with the web component API
let nc = document.getElementsByTagName('notification-center-component')[0];
nc.onLoad = () => console.log('hello world!');
Enter fullscreen mode Exit fullscreen mode

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

Comments 33 total

  • Nevo David
    Nevo DavidMay 10, 2023

    Great stuff Sumit!
    Thank you for that

    • Sumit Saurabh
      Sumit SaurabhMay 10, 2023

      Thank you for going through my article, Nevo! 😊

  • Abdulsalaam Noibi
    Abdulsalaam NoibiMay 10, 2023

    Wow,what an amazing and well detailed article. I need to add a notification functionality on my blog project.

    • Sumit Saurabh
      Sumit SaurabhMay 10, 2023

      Thanks, Abdulsalaam!

      This could come-in really handy for you.

      Do join our discord community if you get stuck anywhere! 🚀

  • Muhammad Ridwan Hakim, S.T.
    Muhammad Ridwan Hakim, S.T.May 11, 2023

    Thank you so much.

  • gopalece1986
    gopalece1986May 11, 2023

    Wow nice

  • Nazar
    NazarMay 11, 2023

    Just wow!
    Cool article content + nice design with animation = 🤩 🤯 😍
    Good job @sumitsaurabh927 !

    • Sumit Saurabh
      Sumit SaurabhMay 13, 2023

      Thanks a lot for those generous words Nazar! 😊

  • Hamza Ghazouani
    Hamza GhazouaniMay 11, 2023

    thank you so mush

  • Arowolo Ebine
    Arowolo EbineMay 12, 2023

    Great!!! Cool, it was really details and easy to understand.

    Thanks bro

    • Sumit Saurabh
      Sumit SaurabhMay 13, 2023

      Thank you for reading the article, Arowolo!

      We put new articles every 10 days or so, make sure you follow us to not miss out on them.

  • solowon27
    solowon27May 12, 2023

    Simple and brilliant 👌

    • Sumit Saurabh
      Sumit SaurabhMay 13, 2023

      That’s what I was aiming for!

      Thanks for going through it, Solowon

  • usando
    usandoMay 13, 2023

    Very nice hope to use on projects... Thank you for sharing

    • Sumit Saurabh
      Sumit SaurabhMay 15, 2023

      So happy to share it with you! I hope you use it in a project soon

  • Aim23
    Aim23May 14, 2023

    Looking great. As it has web component integration I would be able to use this in my VBasic Webapplication.
    What I'm curious about is, how to integrate such notifications by differentiate by logged in users who are eligible to receive a certain notification which is meant for this user.
    Are there any tutorials out?
    All the best and thanks again for the fish!

  • zilvester
    zilvesterMay 14, 2023

    Works on iOS? Probably no as apple hates webapps because they dodge the app store and big apple can't profit..

    • Sumit Saurabh
      Sumit SaurabhMay 15, 2023

      You can still use Novu to send notifications using APN on ios devices

      • zilvester
        zilvesterJun 3, 2023

        Sorry only just seen this 😵‍💫🫡

    • zilvester
      zilvesterJun 3, 2023

      But seriously Sumit, you have made a great how-to, and thanks... but does it work on iOS? What about the VAPID key and all that? Cheers

      • Sumit Saurabh
        Sumit SaurabhJun 13, 2023

        Thanks for your generous words! Really appreciate it and sorry for replying this late, just saw it.

        As for VAPID key, we don't currently support it out of the box the workaround it is that firebase supports VAPID key and if you're using firebase as a provider in Novu, it works!

        If you want to use Novu in native ios apps, we do support it and you can get started here:
        docs.novu.co/channels/push/apns/

        Hope I was able to help and if you have any more questions, please drop them below. 😊

  • DevMirza
    DevMirzaMay 14, 2023

    The steps are not well described and the code for front-end and back end not available too!

    • Sumit Saurabh
      Sumit SaurabhMay 15, 2023

      What exactly was not clear in the steps Mirza? Can you point to something specific? I’d love to make it as clear as I can.

      As for the code, it was my mistake, I’d mistakenly made the repositories private.

      I’ve now made it public now, thanks for pointing it out.

      You can also take a look at this link to go through more such projects in different technologies in addition to the one I’ve shared above:

      github.com/novuhq/examples

  • Hector Sosa
    Hector SosaMay 15, 2023

    Incredibly detailed post! Great stuff! Definitely bookmarked! @sumit I've built a OSS tool for creating engaging screenshots with ease. Check it out and let me know what you think! Cheers!

    github.com/ekqt/screenshot

    • Sumit Saurabh
      Sumit SaurabhMay 15, 2023

      Thanks for those kind words, will check out what you’ve built for sure! 🚀

  • Nwosa Tochukwu
    Nwosa Tochukwu May 18, 2023

    Well detailed.. Thanks...

    It will come in handy

  • Erik-NA7
    Erik-NA7May 19, 2023

    I built in-app real time notification using only socket.io, much simpler than this.

    • Sumit Saurabh
      Sumit SaurabhMay 22, 2023

      Can you share a bit more? What features did you implement? What if I wanted email notifications or sms notifications or chat or in-app notifications? Can I do that easily using socket.io?

  • khaybee24
    khaybee24Mar 27, 2024

    Good one 👍

Add comment