nodejsbanner

Tutorial: How to create a basic chat with node.js

Because I found it hard to find any basic beginner tutorials for node.js when I searched for them, I’ll now do my best to provide one myself.

I’m going to show you how to create a simple chat application, which just transmits messages from one client to all others. The only special node.js module we’ll be using is socket.io, and I promise I’ll keep it very simple and easy to understand. It’ll be alot of explaining and little code. You’ll still need to have basic javascript knowledge though.

1. Preperations

After you’ve successfully installed node.js on your server,  the first thing to do is to create a new directory for our chat project. Inside that directory, create a file named main.js, which will later be run by node.js as our server.

Technically you don’t have to name the file “main.js”. You don’t even really need the “.js” extension, but I like to name it that way, so I can instantly tell that it’s the main server file of a web-app, which can be run by node.js.

Also create another new file in the same directory and name it client.html. We will later fill it with the code for the chat client, that the users will see in their browsers.
But first, let’s script the server side.

2. Serving a file

So we’ll open main.js and start by loading the http module coming with node.js, which we need to handle and answer all http requests coming from the clients, and in this case, sending the client.html file back to the user. To load a module we use the require function:

var http = require('http');

We’ll also need to actually read the contents of client.html from the filesystem, which is done with the fs module in node.js. So we’re gonna load this too:

var http = require('http'),
    fs = require('fs');

The http module provides us with the createServer method. It allows us to specify a callback function that will be called every time somebody sends some kind of http request to our server.

var app = http.createServer(function (request, response) {

}).listen(1337);

We get two objects handed to our callback function. The request object contains all infos about the http request the server received, and the response object is used to send our answer back to the user.

Then, right after creating the server (and storing it in the variable app) we call it’s listen() method to start it. All we have to tell listen() is the port the server shall listen for requests on, and of course it should be a free port.

Now, since we only have one .html file to serve in our case, we can just reply with that to ALL requests we get, so our users would only have to enter “example.com:1337″ into their browsers to get the chat client.
We’ll read the file using the readFile() method of the fs module we’ve already loaded at the beginning.

var app = http.createServer(function (request, response) {
    fs.readFile("client.html", 'utf-8', function (error, data) {
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(data);
        response.end();
    });
}).listen(1337);

We hand the readFile() method 3 parameters. The first parameter is just the name of the file we want to read. The second one is the encoding of the file (in most cases you’re good to go with utf-8), and the last one is a callback function, which will be called when the attempt to read the file is done.

This callback function gets the parameters error and data. To simplify our chat example a little, we’re gonna ignore the possibility of there being an error, and only use the data paremeter, which contains the contents of our file. Note: You should always check for errors when you’re writing a server that’s gonna go public.

Inside the callback function we use the response parameter of our server callback, to send the data, we just read from client.html, back to the user.

We always have to begin our responses with response.writeHead(). First, we have to tell it the HTTP Status Code our answer should have. 200 stands for OK, which you’ll use in most cases where there hasn’t been an error or some other special case. Next we hand writeHead() the Header Information that shall be sent. In most cases it’s enough to just specify the MIME-type of the contents we’re going to send, and because client.html will contain HTML code, we set it to “text/html”. Then we use response.write() to send the contents of client.html to the user, and finally we finish the process with response.end(), which also has to be called after every response.

3. Handling Message Events

Here’s whats gonna happen when a user posts a message to the chat: His client will send the message to our server, and the server will then forward it to all other connected clients, which then see the message appear in their browsers.

To get messages from the client to the server we could use classic AJAX to just send more HTTP requests, and … yeah, that’s it. We can’t do anything more with AJAX than sending HTTP requests from the client to the server. But we need to somehow get messages, that other users want to post, from the server to all the clients as well. And here we’ve got a problem: the clients do not know when other users post messages, or if they posted something at all. Which means the clients do not know when to ask the server for new messages.

A workaround could be to force all clients to ask the server for updates in fixed intervals, every 15 seconds for example. Anything but a good solution. Luckily there is a better way to do it.
Socket.io is a module for Node.js that simplifies all those things for us. It allows us to create a socket connection between the server and clients and call events and send data back and forth without any trouble.

socketiov9

To use socket.io, we first have to install it via the node package manager. Here’s how to install modules with NPM: link. Just open a terminal, go to our project directory and type:

npm install socket.io

To create our server side Socket.io instance we’ll use the modules listen() method. And because we won’t need the module itself anymore we omit the step of storing it in a variable and instead directly store the socket.io instance. Like that:

var io = require('socket.io').listen(app);

The listen() method accepts either a port and lots of different settings, OR simply our http-server instance, which I find alot easier (since we already have it, you know :)). We simply insert this behind our http.createServer() call and socket.io is ready to use.

Now we’ll set up our event, which will be fired by the client and which our server will respond to:

io.sockets.on('connection', function(socket) {
	socket.on('message_to_server', function(data) {
		io.sockets.emit("message_to_client",{ message: data["message"] });
	});
});

I’ll explain that one at a time.

io.sockets.on(‘connection’,function(socket))

Sets an event handler that will fire everytime a client socket connects to the servers socket.io module. Socket.io will then create a counter piece to the clients socket and pass it to the event handler.
So the socket we get here is the direct link to the counter-piece-socket of the client, which just connected to the server. We can use this socket to trigger events on the client side and send data to it. Extremely easy and painless.

socket.on(‘message_to_server’, function(data))

Sets an event handler on that counter-piece-socket, that will be called everytime the one connected client triggers the event “message_to_server” (and it will trigger it whenever it wants to send a message). The event handler gets the parameter “data”, which will contain all data that has been passed along with that event, which will in our case be the chat message that the server has to broadcast.

io.sockets.emit(“message_to_client”,{ message: data["message"] })

Will trigger the event “message_to_client” for ALL client sockets, and send the message, which we’ve just received from one specific client, along.

So what all this does is: Receiving a message from one client and broadcasting it to all clients. -> Perfect.

Our main.js file should now look like this:

var http = require('http'),
	fs = require('fs');

var app = http.createServer(function (request, response) {
	fs.readFile("client.html", 'utf-8', function (error, data) {
		response.writeHead(200, {'Content-Type': 'text/html'});
		response.write(data);
		response.end();
	});
}).listen(1337);

var io = require('socket.io').listen(app);

io.sockets.on('connection', function(socket) {
	socket.on('message_to_server', function(data) {
		io.sockets.emit("message_to_client",{ message: data["message"] });
	});
});

Looks pretty compact after all :)

4. Writing the client

The basic skeleton of our client looks just like that of any other html page, with the exception that we include the socket.io client-side library. Open client.html and add:

<!DOCTYPE html>
<html>
    <head>
        <script src="/socket.io/socket.io.js"></script>
        <script type="text/javascript">
            // our socket.io code goes here
        </script>
    </head>
    <body>
        <input type="text" id="message_input"/>
        <button onclick="sendMessage()">send</button>
        <div id="chatlog"></div>
    </body>
</html>

Im not gonna explain the contents of the body, because thats not even related to node.js and socket.io. It’s the bare minimun necessary to test our server.

The first thing our client has to do, is to create a socket connection to the servers socket.io module.

var socketio = io.connect("127.0.0.1:1337");

If the server is running on your local machine for testing, you can use the 127.0.0.1 IP. But as soon as you want peoplez from the interwebz to be able to see your site, you need to change the IP to the public domain/IP of the machine your node server is running on.
(Also don’t forget to declare the port!)

Next we’ll have the client react when the server sends us messages:

socketio.on("message_to_client", function(data) {
    document.getElementById("chatlog").innerHTML = ("<hr/>" +
    data['message'] + document.getElementById("chatlog").innerHTML);
});

Just like on the server side we set an event handler that’ll be called when the client receives an event. This time the event is called “message_to_client” and the handler again receives the data that has been passed along with the event. The event handler then adds the received message to the chatlog on the site. Note: the user who sends a message will receive his own message back from the server, so we don’t have to add messages we send to the chatlog, but only those we receive. Very handy.

The last thing we have to do is to implement sending messages to the server. We do this using the exact same emit() method we used for sending messages from the server to the clients.

function sendMessage() {
    var msg = document.getElementById("message_input").value;
    socketio.emit("message_to_server", { message : msg});
}

Trigger event “message_to_server”, pass along chat message for the server to broadcast, done!

Let’s test it!

Open a terminal, go to our project directory and type:

node main.js

Now you can just open our chat client in any browser by typing in the IP and the port. For example “127.0.0.1:1337″, and there we go! You should see all messages you send appear below the input box.
And the exciting part: You’ll also see the messages other people write when they use the page.

basicnodechat

You can test this for example by opening two browser windows and send messages around between them. Note: sometimes browsers won’t open 2 pages with the same url. In that case append a # like “http://127.0.0.1:1337/#”. That might fix it.

WARNING: You should not make this example publicly accessible, as it’s vulnerable against XSS attacks.

5. Security

The chat itself will now work fine, and you can skip this part if you only wanna test it on your local machine, but in case you wanna show it to other people over the internet, we’ll have to fix a giant vulnerability first: Users can send HTML-code to the other clients which will then be interpreted by their browsers. This way people can execute javascript and do whatever they want with browsers who visit your chat. Definitely not a good thing.
You can test this by typing “<b>test</b>” in the chat box. If you see bold text thats not a good sign.

Luckily there’s a super easy way to fix this: a module called node-validator, that can escape HTML-code in strings, so it can be sent to our clients without being interpreted.

Install it on the server like this:

npm install validator

Then load the modules sanitize object in our main.js:

var http = require('http'),
    fs = require('fs'),
    sanitize = require('validator').sanitize;

And escape received messages before broadcasting them in our “message_to_server” event:

io.sockets.on('connection', function(socket) {
    socket.on('message_to_server', function(data) {
        var escaped_message = sanitize(data["message"]).escape();
        io.sockets.emit("message_to_client",{ message: escaped_message });
    });
});

Done. When we run this, received html will be escaped and no longer be executed in the browsers. We should no longer receive bold text when we send “<b>test</b>”, but instead see the <b> tags.

Here are the files from this tutorial: nodejs_demochat.zip

6. Your turn!

Now you can go on and improve the chat, add features, make it look better, etc. Here are some ideas for you:

  • Sanitize user input
  • Show how many people are online
  • Show at what time the messages have been posted
  • Manage user accounts
  • Keep the chatlog on the server
  • Private chat between two users

I’ve also made another version and implemented a few of those ideas. You can check it out here: http://arminboss.de:1337/

If you create something cool, post it in the comments section.
Have fun and success!

43 thoughts on “Tutorial: How to create a basic chat with node.js

  1. Why would you add the: “WARNING: You should not make this example publicly accessible, as it’s vulnerable against XSS attacks.” but not actually just fix the demo code??

    1. I want to keep it a very simple example for node.js/socket.io. I linked a module though which helps sanitizing Input: https://github.com/chriso/node-validator.

      Just install that and use it’s escape() method to sanitize all messages the server receives before storing or broadcasting them.

      Edit: I added a little section to clarify this.

  2. Nice tutorial!
    I’m having trouble with adding a .css stylesheet to the client.html file!

    <link rel=’stylesheet’ type=’text/css’ href=’/main.css’>

    in client.html doesn’t work. I’m not sure how to make this work. I’d like some help here!

    1. You can’t link CSS files because they won’t be served by node.js. In this example the server always answers with client.html to all requests. No matter if your browser asked for it or for something else like a CSS file.

      Serving files with node.js is a different topic than network messaging. Heres some reading for you:

      http://fromanegg.com/post/18352790356/how-to-make-a-node-js-static-file-server-part-1

      You should also take a look at the expressjs framework:
      http://expressjs.com/guide.html

      A quick and dirty way to add CSS to the chat would be to just add it to the client.html file.

  3. Hi, i was looking like two days for some tutorial for dummies who just started with node.js and everything and this tutorial is aaabsolutly amazing :) I litterly love the way how you describe everything because in lot of tutorials they just put the code write few lines and the point is missing. This was definely not that case :) Would be greate if you would make as soon as posiblle some bigger tutorial where you would show how to make a page with css etc but not just putted it inline to the client.html and what is more intresting… how to make some chats private? Lets say i want to make private rooms or … this example send messages to all users connected but i am not sure how to separate them to some groups like “admin socket” or idk something like that :P Really great tutorial!

    1. Hi Trki. I’ve been thinking about writing an advanced tutorial about this topic, but i’m currently very busy and i don’t know when i’ll find the time. Thanks for the input though!

  4. Hey i did the same as you because im new to node.js just learned the basics and wnated to get into the socket.io but everytime i try to run it i get that i can’t call a methon ‘on’of undefined in this part : io.socket.on(‘connection’, function(socket) what might be the problem ?

    1. Sounds like a typo. If thats really your line of code then you’d have to use io.sockets.on (plural) instead of io.socket.on.

  5. Hi, I wonder how did you get your sample chat to work under 3G connection from cellphone. I used the code on your demo and built a nodejs demo but it didnt work on my 3g network from iphone. But when i use the same iphone to visit your arminboss.de:1337, it worked. Can you post a server side node.js script of arminboss.de:1337 if possible? thank you

    1. The JS library is automatically being served by the the socket.io module to localhost:<port>/socket.io/socket.io.js

  6. Where in the code do you store the messages, from your code I don’t see it, I see that every time a message get send you return this message to all the clients and overriding old messages

  7. I tried to run your code from an AWS Ubuntu instance.
    After I run node main.js I get the message
    info – socket.io started

    And whenever I connect with my website I get the message
    debug – served static content /socket.io.js

    As a result of this I can not a proper working chat site.

    I have tried both
    var socketio = io.connect(“http://localhost:8080″);
    and
    var socketio = io.connect(“http://ec2-54-218-38-62.us-west-2.compute.amazonaws.com:8080″);

    and also both
    as well as

    Do you know what the problem could be?

    Regards,
    Ahmet

    PS: Really awesome explanation!

    1. debug – served static content /socket.io.js

      should be happening because of

      <script src=”/socket.io/socket.io.js”></script>

      which shouldn’t be a problem. Thats what you want.

      What problems do you have excactly?

  8. The main problem is that submitting something does not have any effect. Not on the client side or the SSJS side…

    1. Maybe your server listens on the wrong port. In my example i use port 1337. Your code uses 8080. But i’d need to see your code to tell you more.

      Edit: Thats probably not it, because in that case you wouldn’t even get the user interface, unless you browse to port 1337 and then for some reason try to connect socket.io to 8080.

      1. I feel like such an ass.
        First I though my server on AWS was missing some kind of recourse, even though I manually installed the socket.io dependency. So I tried it to deploy it on Heroku. Of course there are a few alterations you have to make for Heroku (such as writing the Package.json and Procfile documents). Furthermore Heroku dynamically assigns a port and adds it to env, so you have to pull it from there with .listen(process.env.PORT || 5000).

        Even with Heroku I could not get it to work, while I could get it to work on localhost. After carefully checking my code I saw a typo:

        Thanks again and keep the good tutorials coming! :)

  9. On my example, I appended the message as an text node via “document.createTextNode()”.
    That wouldn’t solve an XSS attack, would it?

  10. I ran the chat on my Mac and worked fine. I tested connecting two browsers and both sent data.

    When I tried to connect through my iPhone it didn’t work. I’m totally sure I wrote the ip right, but it simply doesn’t work.

    Do you know why?

    I tried changing the port, but I got same result.

    1. No i’m not sure. It’s possible that your phone doesn’t support any of the websocket alternatives socket.io provides. But at least AJAX long polling should have worked i think.

  11. Great tutorial. Thanks a lot… even if it isn’t working :(

    I set up everything but when I try to test my client I can’t “send” the message… The Terminal gives me:
    speedport_w723_v_typ_a_1_00_098:Noding around Bambi$ node main.js
    info – socket.io started

    when I enter “127.0.0.1:1337″

    debug – served static content /socket.io.js
    debug – client authorized
    info – handshake authorized 1EzQ0JEjHFC7uiqfxtQf
    debug – setting request GET /socket.io/1/websocket/1EzQ0JEjHFC7uiqfxtQf
    debug – set heartbeat interval for client 1EzQ0JEjHFC7uiqfxtQf
    debug – client authorized for
    debug – websocket writing 1::

    pops up in the console and the Button can be clicked but no message is displayed ?

    Any ideas?

    main.js looks like shown, client:

    var socketio = io.connect(“127.0.0.1:1337″)
    socketio.on(“message_to_client”, function(data){
    document.getElementById(“chatlog”).innerHTML = (“” + data['message']+ document.getElementById(“chatlog”).innerHTML);
    });

    function sendMessage(){
    var msg = document.getElementById(“message_input”).value;
    socketio.emit(“message_to_server”,{message : msg});
    }

    send

  12. Hi, this tutorial looks very nice and you explained it very good. But I don’t know why it’s not working for me.
    I made all the steps but I’m getting an error. I’m working with Eclipse but I don’t think, that this is the problem. Here my error from Eclipse:

    D:\eclipse-workspace32\ChatServer\main.js:20
    var socketio = io.connect(“127.0.0.1:1337″);
    ^
    TypeError: Object # has no method ‘connect’
    at Object. (D:\eclipse-workspace32\ChatServer\main.js:20:19)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3

    I further have to say, that my socket.io.js file is in another folder structure: it’s in node_module/socket.io/lib/socket.io.js

    I changed it in the client.html file but still getting the same error. Does anyone know how to fix this problem?

    Thanks in advance!

    1. Ok my fault, I fixed it! I think I read to fast and overlooked that the last code comes in the client.html file into the -tag.

      So problem is fixed! :D

  13. I’m completely new to this so might be making a rookie error here, but I received errors implementing the validator code as you’ve written above.

    The only way I could get it to work correctly was using turning it around completely to:
    sanitize = require(‘validator’);
    &
    var escaped_message = sanitize.escape(data["message"]);

    This is on Win8, x64, node v0.10.26. It’s all working now fine for me so no worries, just thought you’d like to know in case others are having errors too.

  14. I used webstorm for a hasty trial and found it easily executable. Afterward went thru your tutorial and found it great.i was trying on jquery.but this way it is simple and fantastic.Thanks a lot.

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>