Real time notification system using Socket.io

Real time web/mobile application are becoming popular day by day. Services like Firebase and Pusher provides API’s and Services to develop effective real-time notification system for your mobile and web apps.

We are not going to use these Services in this post; instead we will develop application that pop up notification on particular event – Say a new comment added on Post. For notification we will use Chrome desktop notification and for real-time communication – Socket.io.

I have already covered desktop notification here and Socket.io tutorial here.

DOWNLOAD

Prerequisites :

Level of tutorial is Medium. Knowledge of Node.js and Socket.io is required.

Points to be covered :

  • Installation and Setup.
  • Application flow.
  • Database design.
  • Setting up Grunt.
  • Server.
  • Client.
  • Socket.io integration.
  • Notification integration.
  • Running the app.

Installation and setup.

We need following node modules :

  • socket.io
  • mysql
  • express
  • grunt
  • grunt-nodemon
  • grunt-jshint

Create new package.json file and paste following code. Use npm init to create package.json file;It’s a best practice.

package.json
{
  "name": "socket-notification",
  "version": "1.0.0",
  "description": "Real time notification system using Socket.io and ExpressJS",
  "main": "./Server/",
  "scripts": {
    "start": "node ./Server/"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.13.3",
    "socket.io": "^1.3.6",
    "mysql": "~2.9.0"
  },
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-jshint": "^0.11.3",
    "grunt-nodemon": "^0.4.0"
  }
}

Install dependencies using

npm install

Directory setup.

--- Server
   + -- index.js
   + -- db.js
--- Routes
   + -- index.js
--- Client
   + -- index.html
--- node_modules
--- package.json
--- GruntFile.js

Application Flow :

To keep the demo simple, I have one constant status in database and user will add comment on that. As soon as some one posted a comment all users except the one who have made the comment will receive desktop notification immediately.

Socket.io will be responsible for detecting events and information back-end as well as client about same.

Database design :

Database is in MySQL and it’s quite simple; It contains 3 tables and they store Status, User and Comment information. Comment is linked to User table with foreign key. Status table linked to User table too with foreign key so that we ensure only registered User can add Status and Comments.

Here is the Database schema. ( SQL file is provided with Source code. )

ER Diagram for real time notification system

Open PHPMyAdmin and create new Database name as socketDemo and then import the SQL file provided in Source code. This will create tables and add data needed to run the application.

Setting up Grunt.

Grunt is one of the popular task runner module used for running various tasks ( Code quality checking, uglify etc ) before starting the Node Server. This makes sure your code passes various task before it actually runs. Here is following task we will run using Grunt.

  • Passing code to JsHint to check quality.
  • Running the app using NodeMon.

To read more about Nodemon click here.

You need to install Grunt globally before using it. Here is command to install it.

sudo npm install grunt-cli -g

Once installed, create GruntFile.js in project main directory ( Assuming you have installed all modules mentioned above ) and paste following code.

GruntFile.js
module.exports = function(grunt) {
    grunt.initConfig({
        pkg : grunt.file.readJSON('package.json'),
        jshint : {
            myFiles : ['./Server/<strong>/*.js','./Routes/</strong>/*.js']
        },
        nodemon : {
            script : './Server/'
        }
    });
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-nodemon');
    grunt.registerTask('default', ['jshint','nodemon']);
};

We have everything ready. Let’s create our Server.

Server.

We are using Express to create Web Server for us. Here is our Server code stored in Index.js.

Server/index.js
var express = require('express');
var app     = express();
var path    = require("path");
var mysql   = require("mysql");
var http    = require('http').Server(app);
var io      = require('socket.io')(http);
var router  = express.Router();

/* Creating POOL MySQL connection.*/

var pool    =    mysql.createPool({
      connectionLimit   :   100,
      host              :   'localhost',
      user              :   'root',
      password          :   '',
      database          :   'socketDemo',
      debug             :   false
});

// Require Database operation and router files.

var db      = require("./db");
var routes  = require("../Routes/")(router,mysql,pool);

app.use('/',router);

http.listen(3000,function(){
    console.log("Listening on 3000");
});

Run the app by using following command.

grunt

Here is our router file.

Routes/index.js
var path = require("path");
module.exports = function(router,mysql,pool){

router.get('/',function(req,res){
    res.sendFile(path.join(__dirname,'../Client','/index.html'));
});

router.get('/getStatus',function(req,res){
    pool.getConnection(function(err,connection){
        if (err) {
           connection.release();
           return res.json({"error" : true,"message" : "Error in database."});
        } else {
           var sqlQuery = "SELECT * FROM ??";
           var inserts = ["UserPost"];
           sqlQuery = mysql.format(sqlQuery,inserts);
           connection.query(sqlQuery,function(err,rows){
              connection.release();
              if (err) {
                 return res.json({
                          "error" : true,
                          "message" : "Error in database."
                      });
            } else {
               res.json({"error" : false,"message" : rows});
            }
           });
        }
        connection.on('error', function(err) {
           return res.json({"error" : true,"message" : "Error in database."});
        });
    });
});
}

Client.

Here is our view part. I have not use any fancy CSS here and i am using jQuery to do ajax call.

Client/index.html
<html>
<head>
<title>Socket notification</title>
</head>
<body>

<div id="status">
</div>

<div id="commentBox">
<input type = "text" id = "name" size = "40" placeholder="Your name - put shahid"><br><br>
<textarea cols = "38" rows = "10" id = "comment" placeholder="Add your comment"></textarea><br><br>
<input type = "button" id = "addComment" value = "Comment"><br>
<span id = "message"></span>
</div>

</body>
<style media="screen">
    body {
        padding : 50px;
    }
    #status {
        width : 250px;
        padding : 10px;
        font-size : 14px;
        margin-left: 20%;
    }
    #commentBox {
        width: 250px;
        padding: 10px;
        margin-top : 10px;
        margin-left : 20%;
    }
</style>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</html>

This is the basic view. We will add ajax calls, socket and notification in next section.

Socket.io integration.

We have our Server and Client ready. Let’s add Socket support. In Server.js file, we have required socket in io variable. Let’s use it to develop our simple socket handler.

First of all we need to recognise Is the user connected ? This code will do.

io.on('connection',function(socket){
// This event will trigger when any user is connected.
// You can use 'socket' to emit and receive events.
});

Next step is we need to detect whether particular event happened or not ? In our case, **”did some one added a comment ?” **.

Let’s name this event as ‘comment added’, so in order to listen this in back-end you need to use following code.

io.on('connection',function(socket){
// This event will trigger when any user is connected.
// You can use 'socket' to emit and receive events.
socket.on('commend added',function(data){
// When any connected client emit this event, we will receive it here.
});
});

In order to generate any event, we can use .emit() function and if you want to emit event to broadcast it to everyone but not the one who have generated you need to use following code.

io.on('connection',function(socket){
// This event will trigger when any user is connected.
// You can use 'socket' to emit and receive events.
socket.on('commend added',function(data){
// When any connected client emit this event, we will receive it here.
io.emit('something happend'); // for all.
socket.broadcast.emit('something happend'); // for all except me.
});
});

Here is updated Server code.

Server/index.js
var express = require('express');
var app     = express();
var path    = require("path");
var mysql   = require("mysql");
var http    = require('http').Server(app);
var io      = require('socket.io')(http);
var router  = express.Router();

/* Creating POOL MySQL connection.*/

var pool    =    mysql.createPool({
      connectionLimit   :   100,
      host              :   'localhost',
      user              :   'root',
      password          :   '',
      database          :   'socketDemo',
      debug             :   false
});

var db      = require("./db");
var routes  = require("../Routes/")(router,mysql,pool);

app.use('/',router);

http.listen(3000,function(){
    console.log("Listening on 3000");
});

// Handle socket operation.
// On any connection listen for events.

io.on('connection',function(socket){
    console.log('We have user connected !');
        // This event will be emitted from Client when some one add comments.
    socket.on('comment added',function(data){
                // Add the comment in database.
        db.addComment(data.user,data.comment,mysql,pool,function(error,result){
            if (error) {
                io.emit('error');
            } else {
                // On successful addition, emit event for client.
                socket.broadcast.emit("notify everyone",{user : data.user,comment : data.comment});
            }
        });
    });
});

Here is the db.js file with addComment function.

/Server/db.js
var addComment = function(user,comment,mysql,pool,callback) {
    var self = this;
    pool.getConnection(function(err,connection){
        if (err) {
            connection.release();
            return callback(true,null);
        } else {
            var sqlQuery = "INSERT into ?? (??,??,??) VALUES ((SELECT ?? FROM ?? WHERE ?? = ?),?,?)";
            var inserts =["UserComment","UserId","UserName",
                                     "Comment","UserId","User","UserName",
                                      user,user,comment];
            sqlQuery = mysql.format(sqlQuery,inserts);
            connection.query(sqlQuery,function(err,rows){
                connection.release();
                if (err) {
                    return callback(true,null);
                } else {
                    callback(false,"comment added");
                }
            });
        }
        connection.on('error', function(err) {
            return callback(true,null);
        });
    });
};

module.exports.addComment = addComment;

In above code, we are adding into the UserComment table and selecting the name of user from User table.

Here is client side code.

Client/index.html
<html>
<head>
<title>Socket notification</title>
</head>
<body>

<div id="status">
</div>

<div id="commentBox">
    <input type = "text" id = "name" size = "40" placeholder="Your name - put shahid"><br><br>
    <textarea cols = "38" rows = "10" id = "comment" placeholder="Add your comment"></textarea><br><br>
    <input type = "button" id = "addComment" value = "Comment"><br>
    <span id = "message"></span>
</div>

</body>
<style media="screen">
body {
    padding : 50
}
#status {
    width : 250px;
    padding : 10px;
    font-size : 14px;
    margin-left: 20%;
}
#commentBox {
    width: 250px;
    padding: 10px;
    margin-top : 10px;
    margin-left : 20%;
}
</style>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<script>
$(document).ready(function(){
    var socket = io();
        // API to get status from DB.
    $.get("/getStatus",function(data){
        if(data.error) {
            $("#message").empty().text(data.message);
        } else {
            $("#status").text(data.message[0].UserPostContent);
        }
    });
    $("#addComment").click(function(event){
        var userName = $("#name").val();
        var userComment = $("#comment").val();
        if(userName === "" || userComment === "") {
            alert("Please fill the form.");
            return;
        }
                // Here we emit the event and back-end will add it into DB.
        socket.emit('comment added',{user : userName, comment : userComment});
        socket.on('notify everyone',function(msg){
                        // Will explain in next section.
            notifyMe(msg.user,msg.comment);
        });
    });
});
</script>

</html>

Notification integration.

You can see the function notifyMe() that will generate the notification window. I have explained the code in detail here.

notifyMe(user,comment) function
// Let's check if the browser supports notifications
if (!("Notification" in window)) {
  alert("This browser does not support desktop notification");
}
// Let's check if the user is okay to get some notification
else if (Notification.permission === "granted") {
  // If it's okay let's create a notification
var options = {
      body: message,
      dir : "ltr"
  };
var notification = new Notification(user + " Posted a comment",options);
}
// Otherwise, we need to ask the user for permission
// Note, Chrome does not implement the permission static property
// So we have to check for NOT 'denied' instead of 'default'
else if (Notification.permission !== 'denied') {
  Notification.requestPermission(function (permission) {
    // Whatever the user answers, we make sure we store the information
    if (!('permission' in Notification)) {
      Notification.permission = permission;
    }
    // If the user is okay, let's create a notification
    if (permission === "granted") {
      var options = {
              body: message,
              dir : "ltr"
      };
      var notification = new Notification(user + " Posted a comment",options);
    }
  });
}
// At last, if the user already denied any notification, and you
// want to be respectful there is no need to bother them any more.
}

Here is complete client side code.

Client/index.html
<html>
<head>
<title>Socket notification</title>
</head>
<body>

<div id="status">
</div>

<div id="commentBox">
    <input type = "text" id = "name" size = "40" placeholder="Your name - put shahid"><br><br>
    <textarea cols = "38" rows = "10" id = "comment" placeholder="Add your comment"></textarea><br><br>
    <input type = "button" id = "addComment" value = "Comment"><br>
    <span id = "message"></span>
</div>

</body>
<style media="screen">
body {
    padding : 50
}
#status {
    width : 250px;
    padding : 10px;
    font-size : 14px;
    margin-left: 20%;
}
#commentBox {
    width: 250px;
    padding: 10px;
    margin-top : 10px;
    margin-left : 20%;
}
</style>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<script>
$(document).ready(function(){
    var socket = io();
    $.get("/getStatus",function(data){
        if(data.error) {
            $("#message").empty().text(data.message);
        } else {
            $("#status").text(data.message[0].UserPostContent);
        }
    });
    $("#addComment").click(function(event){
        var userName = $("#name").val();
        var userComment = $("#comment").val();
        if(userName === "" || userComment === "") {
            alert("Please fill the form.");
            return;
        }
        socket.emit('comment added',{user : userName, comment : userComment});
        socket.on('notify everyone',function(msg){
            notifyMe(msg.user,msg.comment);
        });
    });
});
function notifyMe(user,message) {
  // Let's check if the browser supports notifications
  if (!("Notification" in window)) {
    alert("This browser does not support desktop notification");
  }
  // Let's check if the user is okay to get some notification
  else if (Notification.permission === "granted") {
    // If it's okay let's create a notification
  var options = {
        body: message,
        dir : "ltr"
    };
  var notification = new Notification(user + " Posted a comment",options);
  }
  // Otherwise, we need to ask the user for permission
  // Note, Chrome does not implement the permission static property
  // So we have to check for NOT 'denied' instead of 'default'
  else if (Notification.permission !== 'denied') {
    Notification.requestPermission(function (permission) {
      // Whatever the user answers, we make sure we store the information
      if (!('permission' in Notification)) {
        Notification.permission = permission;
      }
      // If the user is okay, let's create a notification
      if (permission === "granted") {
        var options = {
                body: message,
                dir : "ltr"
        };
        var notification = new Notification(user + " Posted a comment",options);
      }
    });
  }
  // At last, if the user already denied any notification, and you
  // want to be respectful there is no need to bother them any more.
}
</script>

</html>

Running the app.

Make sure you have configured the database. Open up your terminal and move to project directory and type

grunt

to run the app.

Real time notification using socket.io app

Now open two browser windows and navigate to localhost:3000 to view the app.

Type user name as shahid and add the proper comment. Upon submitting you should see the notification generated on second user browser window.

Real time notification using socket.io

You can add more user by inserting data in User table.

Conclusion :

Real time notifications are catchy part of any web application. There are other Services like Pusher which provides push-notification to both browser and mobile apps. We will cover them in coming posts.

Shahid (UnixRoot) Shaikh

Hey there, This is Shahid, an Engineer and Blogger from Bombay. I am also an Author and i wrote a programming book on Sails.js, MVC framework for Node.js.

Related Posts

47 Comments

  1. http
    Greetings! Quick question that’s entirely off topic. Do you know how to make your site mobile friendly?
    My site looks weird when viewing from my
    apple iphone. I’m trying to find a template or plugin that might
    be able to resolve this issue. If you have any recommendations,
    please share. Many thanks!

    1. Hi interior,

      You can use jetpack plugin of wordpress, In this plugin there is option to create a mobile friendly website in just one click.

  2. I want to make it as a REST API. But socket event is not receiving to server and vice versa.
    I am using angular + express + mongo

  3. I tried this code but it is not working. It give me no error but when I debug the code then found this function
    socket.on(‘notify everyone’, function (msg) {
    notifyMe(msg.user, msg.comment);
    });
    gives the timeout error and notifyMe function is not called.
    Please help if you know solution

    1. try adding this code :
      socket.on(‘notify everyone’,function(msg){
      notifyMe(msg.user,msg.comment);
      });

      outside the click function

  4. Hey Shahid, nice post. Am almost there. Got the app working and db insertions also being done. But I cant get the notification though. There are no errors. Any hints?

  5. Hi,
    In my client index.html if I have commented below line ,still I am getting notification .So I just wanted to what is the use of this below line in index.html
    socket.on(‘notify everyone’,function(msg){
    notifyMe(msg.user,msg.comment);
    });

  6. Hello, Thanks for this wonderful demo.
    I think this code will come outside the click event
    socket.on(‘notify everyone’,function(msg){
    notifyMe(msg.user,msg.comment);
    });

  7. Hi Shahid,

    I ran your code , installed the database and seemingly everything was ok , but to post comments , nothing happens , no error occurs . I am using the version version 51.0.2704.103 m Chrome . Sorry my english

  8. Hey, thanks for the tutorial,

    I receive this error

    node_modules/mysql/lib/protocol/Parser.js:82
    throw err;
    ^

    TypeError: Cannot read property ‘release’ of undefined
    at /Users/me/socket-notification-master/bin/index.js:66:14

    Any ideas?

  9. Hello,
    I have the following warning : “EventEmitter memory leak detected. 11 error lisner added …”. When I click multiple time on the comment button it seems that it goes to an error. Does it initiat another lisner each time I click on “comment” ?
    nb : I don’t get the comment puhed. I don’t know why yet.

  10. i’ve test this and works good, when i push mi first notification, however in the next ones clients are recieveing “++1” notifications (whith the last content) for each time i press “comment” button…. is there a way to solve this?

      1. seems like it initiate a new listen every time i click on “comment”, because the first time i push a comment, it send the notification to me and to any other clients connected to the app…. the second time i click comment, it sends not one, but two notifications to all clients….. the third time, 3 notifications.. and so on…..plus, clients must click once at “comment” button to start recieving the notifications ( i’ve solve this, but not the duplicated notifications issue)

        sry for my bad english, hope u can get my point and help me with this 🙂

        ty so much!

  11. I added the exact code and the so.emmit and so.on not seems to be working. Not insering to DB too.

    My chrome version is 53.0.2785.143 m

  12. You shouldn’t commit the node_modules folder. People dun do that. Let people who download the source to run the npm install by themselves. On windows unzipping the node_modules is breaking coz of long folder and filenames.

  13. Very nice article, can you tell me what changes will I make in order to make a notification system for a mongo database? Will be great help, Thanks!

  14. can you help me on this error ?
    (index):40 Uncaught ReferenceError: io is not defined
    at HTMLDocument. ((index):40)
    at j (jquery.min.js:2)
    at Object.fireWith [as resolveWith] (jquery.min.js:2)
    at Function.ready (jquery.min.js:2)
    at HTMLDocument.I (jquery.min.js:2)

Leave a Reply

Your email address will not be published. Required fields are marked *