DEV Community

Cover image for How to add In-App notifications to any web app!
Sumit Saurabh for novu

Posted on • Edited on • Originally published at novu.co

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

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

Top comments (33)

Collapse
 
clericcoder profile image
Abdulsalaam Noibi

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

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

Thanks, Abdulsalaam!

This could come-in really handy for you.

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

Collapse
 
iamhectorsosa profile image
Hector Sosa

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

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

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

Collapse
 
aim profile image
Aim23

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!

Collapse
 
nevodavid profile image
Nevo David

Great stuff Sumit!
Thank you for that

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

Thank you for going through my article, Nevo! 😊

Collapse
 
solowon27 profile image
solowon27

Simple and brilliant 👌

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

That’s what I was aiming for!

Thanks for going through it, Solowon

Collapse
 
arosebine profile image
Arowolo Ebine

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

Thanks bro

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

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.

Collapse
 
zilvester profile image
zilvester

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

Collapse
 
zilvester profile image
zilvester

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

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

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. 😊

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

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

Collapse
 
zilvester profile image
zilvester

Sorry only just seen this 😵‍💫🫡

Collapse
 
obere4u profile image
Nwosa Tochukwu

Well detailed.. Thanks...

It will come in handy

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

Glad you liked it, Nwosa!

Collapse
 
zaidmaker profile image
DevMirza

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

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

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

Collapse
 
digitalagent profile image
usando

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

Collapse
 
sumitsaurabh927 profile image
Sumit Saurabh

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