Websockets: Chat App with Socket.io and NodeJS

Amazing World of Chat – Illustration by kdonovan_gaddy

Websockets is amazing technology in the new HTML5 specifications that allows for a new way of communicating between browsers and servers. Through this technology, applications can easily and quickly provide bi-directional communication in realtime. This allows developers to create all sorts of realtime applications (e.g. statistics, chat applications) without the need of relying on huge AJAX calls that continuously poll the server and take up large amounts of resources. With Socket.io, working with Websockets is astoundingly easy and works perfectly with the non-blocking and quick realtime server, NodeJS. Plus, with Socket.io, browser compatibility for Websockets is no longer an issue as it solves the problem by choosing whatever communication protocol available for the browser at hand. So, let’s have a peek at parts of an application that uses Socket.io, a program I am developing called LightChat.

The Aim

A chat application is the simplest way of demonstrating the deliciousness of Websockets and Socket.io. For this, we will just need the user to be able to:

  • Send a chat message to be broadcasted to everyone that is ‘connected’
  • Receive a chat message
Plus, if you follow the optional ‘More Complicated’ section, we will be able to let the user create their own chatroom so that it only broadcasts to people at that URL.

This tutorial requires the following software / node modules. Be sure to download them through the links below or through npm.

1. Setting Up

Again, we can set up our project structure as in my previous two tutorials. If you need help with creating this structure again, please have a look at one of my previous posts. Next, we should create our application file in our root folder. I’m calling mine main.coffee:

exp = require 'express'
app = exp.createServer()
io = require('socket.io').listen app

app.configure ->
    app.set 'views', __dirname + '/views'
    app.set 'view engine', 'jade'
    app.use exp.static __dirname + '/public'

app.listen 1337
console.log 'Server running at http://localhost:1337'

If you have seen any NodeJS application before, this will be one of the most simple configurations but in this example, it is simply all we need. The only new thing is perhaps line 3 which requires Socket.io and then configures it to listen to the application created by Express. Apart from that, we are doing everything very standard here.

2. Fun with Socket.IO

Here comes the fun part. When Socket.io makes a connection as in the following script, it figures out the browser and chooses the most appropriate communication method, whether that be Websockets, Flash or whatever. So, we need code that triggers connection and that code that fits in main.coffee:

io.sockets.on 'connection', (socket) ->
    socket.on 'send', (data) ->
        socket.broadcast.emit 'distribute', data

Lines 2 and 3 are the awesomesauce and everything needed to run the chat application. Socket.io works on listeners and senders. However, in the language, these verbs become ‘on’ and ‘emit’, respectively. Line 2 serves as the listener function, waiting for the function ‘send’. When ‘send’ is emitted from the client side (see below), line 3 sends a function named ‘distribute’ which sends the data back. Using the broadcast tag, Socket sends the same data to everyone that is connected except for the user itself.

For the client side, I am going to write a jQuery file called client.coffee under the directory /public/js/:

$ ->
    socket = io.connect 'http://localhost'
    socket.on 'distribute', (data) ->
        $('#results').append('<div>' + data.message + '</div>')

    $('#chatter').on 'submit', (e) ->
        e.preventDefault & e.preventDefault()
        message = $('#message').val()
        socket.emit 'send', {'message': message}
        $('#results').append('<div>' + message + '</div>')

The client side is slightly more involved but no less simple.

In line 1, the variable socket must be connected to a file that is served automatically on the host. If you have installed socket.io correctly (remember this means globally), it should be served when you require it. In my case, it is on http://localhost.

Using the socket variable, we are able to create listener and sender functions through either socket.on or socket.emit. This works exactly the same as the ones on the server. Now, we can define functions such as ‘send’ on line 9, which passes a variable ‘message’ to the server when a form is submitted. As we saw before, the server will be listening for the ‘send’ function and will broadcast the function ‘distribute’ in return. We can define what happens on functions such as ‘distribute’ as in line 3, in which we append the ‘message’ variable to a element called ‘#results’. Simple.

If that has you muddled up, think of the entire process of sending and receiving one chat message. The user types something in a form, submits it and client.coffee emits a function called send, which sends what the user typed as the variable ‘message’ to the server. The server, on main.coffee, listens for the ‘send’ function and when it receives the function, broadcasts a function called ‘distribute’ and sends the data along to everyone except the user. Everyone that is connected has client.coffee which listens for the ‘distribute’ function. On receipt of ‘distribute’, it appends the message to the screen in the ‘#results’ element.

3. Front-End

Now that we’ve created all the logic and understood it (Yes, that was all there was to it), let us create a simple interface for our chat application.

For this, let’s quickly render an index file in main.coffee when one goes to ‘/’:

app.get '/', (req, res) ->
res.render 'index', {locals: {title: 'LightChat'}}

Now, we can create a layout.jade and an index.jade. The layout.jade will be very simple with the bare basics:

!!! 5
html
    head
        title= title
    body
        != body

However, index.jade will be where all the magic happens:

#results
form#chatter
    input#message(type= "text", placeholder= "Type Something...")
    input(type= "submit", value= "Send")

script(src= "/js/jquery.min.js")
script(src= "/js/client.js")

As you can see, it is merely an element called ‘#results’, which we will use to append messages and a form called ‘#chatter’, which the user will type in the ‘message’ input to send to the server. Finally, in the last two lines, jQuery and the client file will be served.

4. More Complicated Please

If you’re complaining that this tutorial was too simple (I was!), here is a little bit more, before you go ahead and run everything. You can follow this if you want or jump right down to the next section if you have had enough. Either way, it should run perfectly well (we hope).

I want to show you how easy it is to create a multiroom chat application wherein users who have entered a chatroom only get the messages from people in the chatroom they are in. To simplify the chatroom experience, however, I am going to use URLs as the identifying factor. This means that separate chatrooms are going to separated by the pathname, everyone who is connected to the same pathname e.g. /abc is going to be in the same chatroom.

So, let’s modify the socket.io code in main.coffee and create a new app.get so that we serve index.jade when going to http://localhost:1337/ and then whatever id.

app.get '/:id', (req, res) ->
    res.render 'index', {locals: {title: req.params.id}}

io.sockets.on 'connection', (socket) ->
    socket.on 'switch', (room) ->
        socket.join room

    socket.on 'send', (data) ->
        socket.broadcast.to(data.room).emit 'distribute', data

You will notice there are some slight differences with this code and the last. Firstly, we grab the ID of the chatroom by calling req.params.id. This only serves to render the index.jade layout and set the title of the page to the ID of the chatroom.

Now, there are two different lines in the socket code. Lines 5 and 6 is where the magic happens. These lines allows us to join a room, when the function ‘switch’ is triggered, a function we are going to set when a person first comes onto a webpage. Then, the last line, Line 9 lets us broadcast the same way as before but only to the room that we are going to specify by adding it in the data.

Now that we’ve changed the server-side, let’s give the server what it needs. The first is to emit a function called ‘switch’ that happens when the user goes to a URL. This function, situated in client.coffee, will pass on the pathname:

room = window.location.pathname.slice 1
socket.emit 'switch', room

Since the pathname contains a slash, I am slicing it to avoid confusion. However, I am pretty sure everything would still run even without it. I am only doing this to keep consistent with the Node side.

Finally, we also have to pass on the room variable when something is sent. To do this, we only have to add the ‘room’ variable when sending in the previous client.coffee:

socket.emit 'send', {'message': message, 'room': room}

Done! Now, you have a chat application capable of multiple chatrooms all separated by simply the URL pathname. Everyone with the same pathname will be able to listen in but users in different pathnames will be in separated chatrooms. Very neat!

5. Running

Again, if you have not got the awesome command line coffee –watch –compile happening, then remember to compile the coffee files before running. Also remember that the client.coffee file needs to compile too. After this, you can go back to your project folder and run:

node main.js

To test it out, I recommend opening two browsers, preferably two different kinds side by side and then posting in one and then posting in the other. And voila! It is amazing in so little code is it not?!

Conclusion

Writing realtime applications like the chat application in this tutorial is amazingly simple with Websockets and Socket.io. And, added with the astounding asynchronous and non-blocking nature of NodeJS, it makes these applications super fast and super simple to write. In this example, we managed to create a chat application in which a user can broadcast a message and users can simultaneously talk to each other with two functions socket.on and socket.emit and around 10 lines of code related to it. With the same functions, any realtime communication between browser and server just became much, much simpler and real fun to write. So, dream up big because realtime communication is now all the more real with Socket.io and NodeJS.

This entry was posted in Tutorial. Bookmark the permalink.
  • nRGB

    This has been really helpful.

    Quality node tutorials are still hard to find, and even harder to find in coffeescript.

    More please…  = )

    • Anonymous

      Thanks! I’m glad you liked the tutorial. I had a hard time looking for good tutorials myself after converting to CoffeeScript and that’s why I decided to write some myself. There will be more coming! Don’t forget to subscribe!

  • Sigurd

     In client.coffee, I think you need to change the event binding function to use bind for #chatter
        $(‘#chatter’).on ‘submit’, (e) ->

    to    $(‘#chatter’).bind ‘submit’, (e) ->

    • Anonymous

      Hi @96036d473862e873e4487d12555fb8bf:disqus, thanks for reading! Actually, from jQuery 1.7 onwards, ‘.on()’ provides all functionality required for attaching event handlers instead of .bind(), .delegate() or .live().  The .on() method is now the preferred method for attaching event handlers so you should use it if you are running jQuery 1.7 or greater.

      • Sigurd

         Thanks for your reply, Alexander. Sorry for my lazyness of not using an updated jquery. I really liked this post, as I had tried other chat examples but couldn’t get them to work. Your approach is simpler, and easier to learn from as well.

  • lee

    Hi I am wondering if you have your code somewhere, I am a newbie to node and socket. Followed your tutorials but when I try to send a message, the message did not appear on both screens. In the console, it says “Uncaught ReferenceError: io is not defined, client.js : line5″ which happens to be this line in coffee, ”socket = io.connect ‘http://localhost”, I suspect is the that socket.io is not installed globally or something, the thing is that I try “npm install socket.io -g”, it throw a bunch of errors i don’t understand, then I tried “npm install socket.io” and it works. Try relinking socket.io and reinstalling socket.io but got into a bunch of errors again I don’t understand. 

    Now I am at a stage where I can run node main.js and go to local…/:id but the send message is not working …

    Could you help me ?

    • lee

      I managet to get socket.io to install globally by using sudo and setting the NODE_PATH=/usr/local/lib/node_modules node variable – thanks to sarnold from Stackoverflow (http://goo.gl/U0o55). Argh still getting the “io is not defined” error, fun times debugging :/

      • Anonymous

        Yep this is exactly what I was going to say in regards to installing the package globally (check my other tutorials out for info). As for debugging, Socket.IO should be hosting a “socket.io.js” on the client side all automatically. You may need to include this line “script(src= “/socket.io/socket.io.js”)” in your jade file before your script.

        • lee

          oh yes, i include it like 5 mins before you post. Now at last I am getting a different error “Cannot read property ‘room’ of undefined” , good progress

          • lee

            Yessss finally got it working, 3 stupid errors, i spell ‘slice’ as ‘slick’ , ‘#results’ as ‘#result’ and instead of socket.on ‘send’, i did socket.send ‘send’ :/

          • Anonymous

            I’m glad you got it working! Thanks very much for reading!

  • Yves

    Thanks for the great tut! I allllmost got it now. Server starts up with this error displayed: “Warning: express.createServer() is deprecated” & “Socket.IO’s `listen()` method expects an `http.Server` instance”

    Is the info in this tut depreciated already? Things change so quickly in the node world.