DEV Community

Cover image for A Starter Guide to Building Real-time Applications with Node.js
Juan Cruz Martinez
Juan Cruz Martinez

Posted on • Originally published at livecodestream.dev on

A Starter Guide to Building Real-time Applications with Node.js

In a world where the value of time is steadily increasing, building applications that users can interact with in real-time has become a norm for most of the developers. Most of the applications we see today, whether they are mobile, desktop, or web applications, have at least a single real-time feature included. As an example, real-time messaging and notifications are two of the most commonly used real-time features used in applications.

In this article, we are introducing you to the development of real-time applications using Node.js. In fact, Node is one of the best programming languages out there to build real-time applications due to its event-driven and asynchronous nature. Before diving into building a real-time application head-first, we’ll see what kind of real-time applications we can build using Node.js.

If you want to know more other areas where Node.js excels or simply falls short, read my article When You Should and Shouldn’t Use Node.js for Your Project.

Where are Real-time Applications Used?

As I mentioned above, messaging and notification delivery are two of the most common use cases for real-time applications. But we can use real-time applications for a myriad of other purposes. Let’s see what they are.

Real-time Messaging

Most of us are familiar with the use of real-time messaging applications, especially in mobile devices, in the form of Whatsapp, Facebook Messenger, and numerous other messaging applications. However, real-time messaging is used not limited to purely messaging applications. We see real-time messaging features in on-demand taxi apps, delivery apps, and collaborative platforms.

Real-time Notification Delivery

Enabling real-time notifications has proved to be a game-changer when it comes to increasing user engagement with applications. For this reason, you would hardly see a modern application that does not deliver notifications in real-time to its users.

Live Streaming

Live streams that users can interact with in real-time are becoming more and more popular after social media platforms integrated live video streams to their applications. Instagram and Facebook live video streaming features are the best examples of this.

Real-time Tracking

With the introduction of popular taxi and delivery applications, like Uber and Amazon, tracking the progress of users’ taxi rides or deliveries in real-time has become an essential requirement. Their real-time progress updates increase the usability and reliability of these applications.

IoT Devices

Real-time features are essential for IoT devices. Data captured by the sensors placed in IoT devices are transmitted, processed, and displayed to the end-users with a minimum delay. Since most of the inputs captured by these devices, like temperature and lighting, constantly change with the time, applications working with IoT devices should be able to receive and send data in real-time.


How can We Build Real-time Applications?

Is building a real-time application different from building a normal web application? The answer is, yes.

Think of a messaging application where users can send messages in real-time. These messages should appear on the other users’ application as soon as the messages are sent. If we implement this application like a normal web application, where only the client can initiate requests to the server to receive data, the user has to either refresh the web page regularly to see the newest messages or the client-side should send AJAX requests to the server in short time intervals to retrieve the newest messages. The former of the two is not very user friendly and the latter is a waste of application resources. Then, clearly, we must have a different method to build real-time applications that makes better sense.

WebSocket provides the solution we need. WebSocket is a communication protocol that allows both the client and server to initiate communication. In other words, with WebSocket, the server can send data to the client any time without the client having to request data first. In the case of the previous messaging application, we can use WebSockets to instantly send messages to all the users through the server. We can use the WebSocket API to communicate using WebSockets when building applications.


Socket.io

However, when implementing a real-time application using Node, we don’t have to directly use the WebSocket API. Instead, Javascript and Node.js library Socket.io, which is an API to the WebSocket API, provides a much simpler implementation of WebSockets for us to use. In this tutorial, we will be using Socket.io to create and manage WebSocket connections between the client and the server.


Building a Real-time Chatroom with Node.js

Now that we have covered the background on real-time application development, we can start creating our own real-time application. In this tutorial, we are going to build a simple chatroom that users can use to communicate with other connected users. Any number of users can connect to the chatroom and the messages one user sends become instantly visible to all the users connected to the chatroom.

Our simple chatroom is going to have the following set of features.

  • Change the username of the user
  • Send messages
  • Show if another user is currently typing a message

Cool, now that we have our requirements, let’s start building up the environment and setting up the structure


Setting up Application Environment

First, create a new directory for the application. Then, run the npm init to set up the package.json file. Make sure that, at this step you assign app.js as your main script, if you didn’t , don’t worry, you can always change it in your package.json at a later point.

Install dependencies

In this tutorial, we are using the express, ejs, socket.io, and nodemon packages to build the application.

  • Ejs is a popular JS template engine
  • We discussed the use of socket.io earlier
  • Nodemon is a package that restarts the server every time we make a change to the application code. It eliminates the need to manually stop and start the server every time we make a change. Unlike the other packages, we install nodemon as a development dependency since we use it only for development purposes.

Install express, ejs, and socket.io using the following command.

npm install express ejs socket.io --save
Enter fullscreen mode Exit fullscreen mode

Install nodemon as a development dependency using this command.

npm install nodemon --save-dev
Enter fullscreen mode Exit fullscreen mode

To start the application with nodemon, we should add a start script to our package.json file.

"scripts": {
    "start": "nodemon app.js",
 },
Enter fullscreen mode Exit fullscreen mode

Then, we can start the application by running the following command on the command-line.

npm run start

Enter fullscreen mode Exit fullscreen mode

If it fails, don’t worry, it’s basically because we don’t have any code file yet.

Set up the application structure

With all the dependencies which will need for this project installed, let’s build app the project structure. For that you’ll need to create a few directories and for now, one file called app.js. Let’s get that done so that your app structure looks as follows:

|--app.js
|--views
|--node_modules
|--package.json
|--public
   |--css
   |--js
Enter fullscreen mode Exit fullscreen mode

I think the structure is pretty clear, but let’s quickly go over it:

  • app.js: file we will use to host our server-side code
  • views: folder containing the views (ejs)
  • node_modules: where we installed our dependencies
  • package.json npm configuration file
  • public: directory we will use to store our assets, like css files, javascript files (for the client side), and images.

First steps building the server

The first we need to do before we even consider doing the real-time connections is to get express up and running, for that, let’s open our app.js file and paste the following code:

const express = require('express')
const socketio = require('socket.io')
const app = express()

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res)=> {
    res.render('index')
})

const server = app.listen(process.env.PORT || 3000, () => {
    console.log("server is running")
})
Enter fullscreen mode Exit fullscreen mode

Once we have express configured and using ejs as template system, we can start working on the sockets.io initialization. For that add the following code at the end of your app.js file.

//initialize socket for the server
const io = socketio(server)

io.on('connection', socket => {
    console.log("New user connected")
})
Enter fullscreen mode Exit fullscreen mode

The code is pretty straight forward, we are initializing socket.io from our server connection (express) and then we set up an even using io.on() which will be triggered every time a new connection to the socket gets established.

If you now run your server with npm start you will be able to receive new socket connections. So let’s start building our front-end.


Building our front-end

We won’t spend much time making our front-end look amazing, but we will explain how the connection to the server works, how to emit and capture socket events and we will apply all of that into our chat example.

Let’s start by creating a template into our views folder, for that create a index.ejs file and paste the following code:

<!DOCTYPE html>
<head>
    <title>Simple realtime chatroom</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <div class="title">
            <h3>Realtime Chat Room</h3>
        </div>

        <div class="card">
            <div class="card-header">Anonymous</div>
            <div class="card-body">
                <div class="input-group">
                    <input type="text" class="form-control" id="username" placeholder="Change your username" >
                    <div class="input-group-append">
                        <button class="btn btn-warning" type="button" id="usernameBtn">Change</button>
                    </div>
                </div>
            </div>
            <div class="message-box">
                <ul class="list-group list-group-flush" id="message-list"></ul>
                <div class="info"></div>
            </div>

            <div class="card-footer">
                <div class="input-group">
                    <input type="text" class="form-control" id="message" placeholder="Send new message" >
                    <div class="input-group-append">
                        <button class="btn btn-success" type="button" id="messageBtn">Send</button>
                    </div>
                </div>
            </div>
        </div>

    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
    <script src="/js/chatroom.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Note how we have included the script of the client-side socket.io library and the custom javascript file we are going to use in this code.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<script src="/js/chatroom.js"></script>
Enter fullscreen mode Exit fullscreen mode

We also have a button with ID messageBtn to send a new message and another button with ID usernameBtn to submit a new username. Username and message inputs have IDs username and message respectively. All user messages are expected to appear inside the unordered list with the ID message-list. If a user is tying a message, that information will appear inside the div with class info.

If you open our browser and you head to http://localhost:3000/ your app will look something like this:

Realtime Chat Room Layout

Realtime Chat Room Layout


But it’s not doing anything, the buttons won’t work and will be pretty much a static application. So next let’s start connecting the front-end to the server.

For that create a new Javascript file named chatroom.js inside the js folder (note in the HTML above, that I’m already referencing to this file) of the public directory. Inside the Javascript file, we need to connect to socket from the front-end. We can do it like this.

(function connect(){
    let socket = io.connect('http://localhost:3000')
})()
Enter fullscreen mode Exit fullscreen mode

Visit your website again and in your terminal (on the server-side) you will see something like:

Terminal log example

Terminal log example


Awesome! your app is already working, though it doesn’t do much. Let’s build functionality next


Changing the user name

The default username we use for every connection is “Anonymous”. We give the users the option to change this username. We’ll set up the back-end to change the username when the front-end emits a change_username event. Go back to your server-side code (app.js) and edit your connection event to add new code.

io.on('connection', socket => {
    console.log("New user connected")

    socket.username = "Anonymous"

    socket.on('change_username', data => {
        socket.username = data.username
    })
})
Enter fullscreen mode Exit fullscreen mode

Next, we need to adjust our front-end, so that when we press the change username button, it emits an event to the server with the name change_username. See how we built the name by emitting and capturing the same event name?

Inside chatroom.js, we are going to add an event listener to usernameBtn to emit a change_username event when the button is clicked.

(function connect(){
    let socket = io.connect('http://localhost:3000')

    let username = document.querySelector('#username')
    let usernameBtn = document.querySelector('#usernameBtn')
    let curUsername = document.querySelector('.card-header')

    usernameBtn.addEventListener('click', e => {
        console.log(username.value)
        socket.emit('change_username', {username: username.value})
        curUsername.textContent = username.value
        username.value = ''
    })
})()
Enter fullscreen mode Exit fullscreen mode

Now if you reload the web page and submit a new username, you will see your current username changed to the new one. Next, let’s start sending messages.


Sending Messages

The next feature we are going to implement is sending messages. Here things start to get a little bit different, so far we said that every time the front-end emits a message the server will receive it, however in our new case, the front-end needs to emit a new_message event, which then will need to be sent to all the connected clients, so that they can print the new message.

First, we will set up the front-end to emit a new_message event when a new message is submitted. Since the client-side should also be configured to receive new messages other users send from the server, the application should also listen to receive_message events on the front-end and show the new message on the web page appropriately. We can achieve both these tasks using the following code which goes inside the previous connect function in chatroom.js.

let message = document.querySelector('#message')
let messageBtn = document.querySelector('#messageBtn')
let messageList = document.querySelector('#message-list')

messageBtn.addEventListener('click', e => {
    console.log(message.value)
    socket.emit('new_message', {message: message.value})
    message.value = ''
})

socket.on('receive_message', data => {
    console.log(data)
    let listItem = document.createElement('li')
    listItem.textContent = data.username + ': ' + data.message
    listItem.classList.add('list-group-item')
    messageList.appendChild(listItem)
})
Enter fullscreen mode Exit fullscreen mode

Every time the receive_message event happens on the client side, we change our DOM to render the message into the screen.

On the back-end side, when we receive a new_message event we need to emit a new event to all the clients, for that we use io.sockets.emit() function. Change your connection event in your app.js file as follows:

io.on('connection', socket => {
    console.log("New user connected")

    socket.username = "Anonymous"

    socket.on('change_username', data => {
        socket.username = data.username
    })

    //handle the new message event
    socket.on('new_message', data => {
        console.log("new message")
        io.sockets.emit('receive_message', {message: data.message, username: socket.username})
    })

})
Enter fullscreen mode Exit fullscreen mode

When handling the new_message event, the server itself emits a receive_message event to the connected clients with data about the new message. This event is received by all the users connected to the server, including the one who sent the message, so that the new message is displayed on their chatroom interfaces.

If you now open your web app in your browser (you can have multiple instances) you can start chatting (with yourself? :p)

You can connect to the chatroom using two separate browsers and play around with the feature of sending messages, and see how the messages one user sends instantly appear on both users’ application interfaces.

Realtime Chat Room - Sending Messages

Realtime Chat Room - Sending Messages



I’m Typing….

In most real-time messaging apps we use today, we see a simple text that says “user X is typing…” whenever another user is typing a message. This gives the application a more real-time feeling and improves user experience. We are going to add this feature to our application.

First, let’s consider the front-end implementation. We add a new event listener to the message input box to emit a typing event whenever a keypress occurs. SInce keypresses on the message input box indicate that the user is typing a message, the typing event tells the server that the user is typing a message. The client-side also listens to typing events emitted by the server to know whether another user is currently typing a message and show it on the user interface.

Again, inside the connect function in chatroom.js, we add the following code.

let info = document.querySelector('.info')

message.addEventListener('keypress', e => {
    socket.emit('typing')
})

socket.on('typing', data => {
    info.textContent = data.username + " is typing..."
    setTimeout(() => {info.textContent=''}, 5000)
})
Enter fullscreen mode Exit fullscreen mode

If one user is typing a message, other users are shown the text “<!-- raw HTML omitted --> is typing…” for 5 seconds.

Now we need to set up the back-end to handle typing events. The code we use here is this.

socket.on('typing', data => {
    socket.broadcast.emit('typing', {username: socket.username})
})
Enter fullscreen mode Exit fullscreen mode

Here, socket.io uses the broadcast function to notify the connected clients. When we use broadcast, every user except the one who is typing the message receives the typing event from the server. So, every user except the one typing the message is shown the text “<!-- raw HTML omitted --> is typing…”.

Again, you can connect to the chatroom from two browsers and see how this works in real-time.

Realtime Chat Room - Typing

Realtime Chat Room - Typing

Awesome!


Summary

Today, using real-time features with desktop, mobile, and web applications has almost become a necessity. In this article, we covered a number of applications of real-time apps and learned how to create a real-time chatroom with the help of Node.js and Socket.io. To continue from here, you can either try to improve this chatroom by adding more features and using a database to persist older messages or implement another real-time application that has a different use case.

Thanks for reading!


If you like the story, please don't forget to subscribe to our free newsletter so we can stay connected: https://livecodestream.dev/subscribe

Top comments (6)

Collapse
 
gokayokyay profile image
Gökay Okyay

Good tutorial, nicely prepared. A word of advice, the problem is there are tons of tutorials on how to build a chatroom with nodejs socket.io, and almost no one mentioned the hard way, direct messaging. I searched it a lot back in 2016, until I found a decent solution. Maybe you can include it in your next tutorial :)

Collapse
 
vickyktk profile image
vickyktk

Hey friend this app has direct messaging features. Please take a look and share your review
realtimechatt.herokuapp.com/

Collapse
 
ferluisxd profile image
Luis Vilca

This is awsome, can I ask what do you user for the users database? and google and facebook login?

Thread Thread
 
vickyktk profile image
vickyktk

Thanks for appreciation 💖💖💖
I use mongoDB for saving data of users and chats.

Collapse
 
fed135 profile image
Frederic Charette

You should check out github.com/kalm/kalm.js . It's lighter, cleaner, faster than sicket.oi and it supports webrtc

Collapse
 
vickyktk profile image
Info Comment hidden by post author - thread only accessible via permalink
vickyktk

This is great blog to get started with Realtime chat application.

realtimechatt.herokuapp.com/

I have developed a chat webapp which has all the feature stated above as well as saving chats to mongodb and retrieving them. It also has secure login system with passportJS .

The GitHub repo is here
github.com/vickyktk/chatBot

Some comments have been hidden by the post's author - find out more