Advertisement
  1. Code
  2. JavaScript
  3. Node

Real-Time Chat With Modulus and Node.js

Scroll to top
This post is part of a series called Getting Started with Modulus.
Build a Real-Time Chat Application With Modulus and Laravel 5
Sponsored Content

This sponsored post features a product relevant to our readers while meeting our editorial guidelines for being objective and educational.

In this tutorial, I will show you how to implement a real-time chat application with Node.js, Socket.IO and MongoDB, and then we will deploy this application to Modulus together.

First of all, let me show you the final look of the application that we will have at the end of the article.

chat-screenchat-screenchat-screen

Node.js will be the nucleus of the application, with Express as the MVC, MongoDB for the database, and Socket.IO for real-time communication. When we've finished, we will deploy our application to Modulus. The MongoDB part actually exists inside Modulus. 

1. Scenario

  1. John wants to use our application, and opens it in the browser.
  2. On the first page, he selects a nickname use during chat, and logs in to chat.
  3. In the text area he writes something and presses Enter.
  4. The text is sent to a RESTful service (Express) and this text is written to MongoDB.
  5. Before writing in MongoDB, the same text will be broadcast to the users that are currently logged in to the chat app.

As you can see, this is a very simple app, but it covers almost everything for a web application. There is no channel system in this application, but you can fork the source code and implement the channel module for practice.

2. Project Design From Scratch

I will try to explain the small pieces of the project first and combine them at the end. I will start from the back end to the front end. So, let's start with the domain objects (MongoDB models).

2.1. Model

For database abstraction, we will use Mongoose. In this project, we have only one model called Message. This message model only contains text, createDate, and authorThere is no model for the author like Userbecause we will not fully implement a user registration/login system. There will be a simple nickname-providing page, and this nickname will be saved to a cookie. This will be used in the Message model as text in the author field. You can see an example JSON model below:

1
{
2
3
    text: "Hi, is there any Full Stack Developer here?"
4
5
    author: "john_the_full_stack",
6
7
    createDate: "2015.05.15"
8
9
}

In order to create documents like this, you can implement a model by using the Mongoose functions below:

1
var mongoose = require('mongoose')
2
3
4
5
var Message = new mongoose.Schema({
6
7
    author: String,
8
9
    message: String,
10
11
    createDate: {
12
13
        type: Date,
14
15
        default: Date.now
16
17
    }
18
19
});
20
21
22
23
mongoose.model('Message', Message)

Simply import the Mongoose module, define your model with its fields and field attributes in JSON format, and create a model with the name Message. This model will be included in the pages that you want to use.

Maybe you have a question about why we are storing the message in the database, when we already broadcast this message to the user in the same channel. It's true that you do not have to store chat messages, but I just wanted to explain the database integration layer. Anyway, we will use this model in our project inside the controllers. Controllers?

2.2. Controller

As I said earlier, we will use Express for the MVC part. And C here stands for the Controller. For our projects, there will be only two endpoints for messaging. One of them is for loading recent chat messages, and the second one is for handling sent chat messages to store in the database, and then broadcast into the channel. 

1
.....
2
3
app.get('/chat', function(req, res){
4
5
    res.sendFile(__dirname + '/index.html');
6
7
});
8
9
10
11
app.get('/login', function(req, res){
12
13
    res.sendFile(__dirname + '/login.html');
14
15
});
16
17
18
19
app.post('/messages', function(req, res, next) {
20
21
    var message = req.body.message;
22
23
    var author = req.body.author;
24
25
    var messageModel = new Message();
26
27
    messageModel.author = author;
28
29
    messageModel.message = message;
30
31
    messageModel.save(function (err, result) {
32
33
       if (!err) {
34
35
           Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) {
36
37
               io.emit("message", messages);
38
39
           });
40
41
           res.send("Message Sent!");
42
43
       } else {
44
45
           res.send("Technical error occurred!");
46
47
       }
48
49
    });
50
51
});
52
53
54
55
app.get('/messages', function(req, res, next) {
56
57
    Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) {
58
59
        res.json(messages);
60
61
    });
62
63
});
64
65
.....

The first and second controllers are just for serving static HTML files for the chat and login pages. The third one is for handling the post request to the /messages endpoint for creating new messages. In this controller, first of all the request body is converted to the Message model, and then this model is saved to the database by using the Mongoose function save.  

I will not dive into Mongoose very much—you can have a look at the documentation for further details. You can provide a callback function for the save function to check whether there is any problem or not. If it is successful, we have fetched the last five records sorted in descending order by createDate, and have broadcast five messages to the clients in the channel. 

Ok, we have finished MC. Let's switch to the View part.

2.3. View

In general, a template engine like Jade, EJS, Handlebars, etc., can be used within Express. However, we have only one page, and that is a chat message, so I will serve this statically. Actually, as I said above, there are two more controllers to serve this static HTML page. You can see the following for serving a static HTML page.

1
app.get('/chat', function(req, res){
2
3
    res.sendFile(__dirname + '/index.html');
4
5
});
6
7
8
9
app.get('/login', function(req, res){
10
11
    res.sendFile(__dirname + '/login.html');
12
13
});

This endpoint simply serves index.html and login.html by using res.sendFile. Both index.html and login.html are in the same folder as server.js, which is why we used __dirname before the HTML file name.

2.4. Front End

In the front-end page, I have used Bootstrap and there is no need to explain how I managed to do that. Simply, I have bound a function to a text box, and whenever you press the Enter key or Send button, the message will be sent to the back-end service. 

This page also has a required js file of Socket.IO to listen to the channel called message. The Socket.IO module is already imported in the back end, and when you use this module in the server side, it automatically adds an endpoint for serving the Socket.IO js file, but we use the one that is served from cdn <script src="//cdn.socket.io/socket.io-1.3.5.js"></script>. Whenever a new message comes in to this channel, it will automatically be detected and the message list will be refreshed with the last five messages.  

1
<script>
2
3
        var socket = io();
4
5
        socket.on("message", function (messages) {
6
7
            refreshMessages(messages);
8
9
        });
10
11
12
13
        function refreshMessages(messages) {
14
15
            $(".media-list").html("");
16
17
            $.each(messages.reverse(), function(i, message) {
18
19
                $(".media-list").append('<li class="media"><div class="media-body"><div class="media"><div class="media-body">'
20
21
                + message.message + '<br/><small class="text-muted">' + message.author + ' | ' + message.createDate + '</small><hr/></div></div></div></li>');
22
23
            });
24
25
        }
26
27
28
29
        $(function(){
30
31
32
33
            if (typeof $.cookie("realtime-chat-nickname") === 'undefined') {
34
35
                window.location = "/login"
36
37
            } else {
38
39
                $.get("/messages", function (messages) {
40
41
                    refreshMessages(messages)
42
43
                });
44
45
46
47
                $("#sendMessage").on("click", function() {
48
49
                    sendMessage()
50
51
                });
52
53
54
55
                $('#messageText').keyup(function(e){
56
57
                    if(e.keyCode == 13)
58
59
                    {
60
61
                        sendMessage();
62
63
                    }
64
65
                });
66
67
            }
68
69
70
71
            function sendMessage() {
72
73
                $container = $('.media-list');
74
75
                $container[0].scrollTop = $container[0].scrollHeight;
76
77
                var message = $("#messageText").val();
78
79
                var author = $.cookie("realtime-chat-nickname");
80
81
                $.post( "/messages", {message: message, author: author}, function( data ) {
82
83
                    $("#messageText").val("")
84
85
                });
86
87
                $container.animate({ scrollTop: $container[0].scrollHeight }, "slow");
88
89
            }
90
91
        })
92
93
    </script>

There is one more check in the above code: the cookie part. If you have not chosen any nickname for chat, it means the cookie is not set for the nickname, and you will be automatically redirected to the login page. 

If not, the last five messages will be fetched by a simple Ajax call to the /messages endpoint. In the same way, whenever you click the Send button or press the Enter key, the text message will be fetched from the text box, and the nickname will be fetched from the cookie, and those values will be sent to the server with a post request. There is no strict check for the nickname here, because I wanted to focus on the real-time part, not the user authentication part.

As you can see, the overall structure of the project is very simple. Let's come to the deployment part. As I said earlier, we will use Modulus, one of the best PaaS for deploying, scaling and monitoring your application in the language of your choice. 

3. Deployment

3.1. Prerequisites

The first thing that comes to my mind is to show you how to deploy, but for successful deployment, we need a working database. Let's have a look at how to create a database on Modulus and then perform deployment. 

Go to the Modulus dashboard after creating an account. Click the Databases menu on the left, and click Create Database. 

Click the Databases menu on the left and click Create Database Click the Databases menu on the left and click Create Database Click the Databases menu on the left and click Create Database

Fill in the required fields in the popup form as below.

Fill in the required fields in the CREATE DATABASE popup formFill in the required fields in the CREATE DATABASE popup formFill in the required fields in the CREATE DATABASE popup form

When you fill in the required fields and click Create, it will create a MongoDB database for you, and you will see your database URL on the screen. We will use MONGO URI, so copy that URI.

Your database has been created and is ready for useYour database has been created and is ready for useYour database has been created and is ready for use

In our project, Mongo URI is fetched from the environment variable MONGO_URI, and you need to set that environment variable in the dashboard. Go to the dashboard, click the Projects menu, select your project in the list, and click Administration in the left menu. In this page, you will see the environment variables section when you scroll down the page, as shown below.

MONGO_URI ValueMONGO_URI ValueMONGO_URI Value

You can deploy to Modulus in two ways: 

  • uploading the project ZIP file by using the dashboard
  • deployment from the command line by using Modulus CLI

I will continue with the command line option, because the other one is easy to do. First of all, install Modulus CLI:

1
npm install -g modulus

Go to your project folder and perform the following command to log in to Modulus.

1
modulus login

When you perform the above command, you will be prompted to enter a username and password:

prompt to enter a username and passwordprompt to enter a username and passwordprompt to enter a username and password

If you have created an account by using GitHub, you can use the --github option.

1
modulus login --github

Now you are logged in to Modulus, and it's time to create a project. Use the following command to create a project:

1
modulus project create "Realtime Chat"

When you run this function, you will be asked for the runtime. Select the first option, which is Node.js, and second you will be asked for the servo size, and you can keep it as default.

Keep servo size as defaultKeep servo size as defaultKeep servo size as default

We have created a project, and this time we will deploy our current project to Modulus. Execute the following command to send the current project to the Realtime Chat project on the Modulus side.

1
modulus deploy

It will deploy your project and you will get your running project URL at the end of the successful deployment message:

1
Realtime Chat running at realtime-chat-46792.onmodulus.net

As you can see, the deployment to Modulus is very easy! 

Modulus CLI has very helpful commands to use during your project deployment or at runtime. For example, in order to tail logs of your running project, you can use modulus project logs tail, to create a MongoDB database use modulus mongo create <db-name>, to set an environment variable use modulus env set <key> <value>, etc. You can see a full list of commands by using Modulus help. 

Conclusion

The main purpose of this tutorial was to show you how to create a real-time chat application with Node.js, Socket.IO, and MongoDB. In order to run a project in production, Modulus is used as a PaaS provider. Modulus has very simple steps for deployment, and it also has an internal database (MongoDB) for our projects. Beside this, you can use very helpful tools inside the Modulus dashboard like Logs, Notifications, Auto-Scaling, Database Administration, and so on.

To sign up for Modulus click here and get an extra $10 exclusively for being a Tuts+ reader. Use promo code ModulusChat10.

For more information on the Modulus enterprise offering click here.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.