Streets Ahead

Presents

Interesting Stuff

CouchDB, WebSockets, and Node.js

Today's post is a quick one, I'm on the road this week so I haven't had a lot of time to prepare posts, but I think I've found something pretty fun. We've been interested in alternative data stores for a while now. Until very recently we've been pretty much all Mongo DB for our projects, and while we still like Mongo a lot, we have started exploring CouchDB a little more lately. CouchDB has some pretty interesting features. One of the things I particularly like is that it has a very simple mostly RESTful API. I say mostly not to criticize the API, but to point at that there are places where the CouchDB developers found that our REST didn't make sense for them so they diverged in a couple places. Regardless they have done a great job at making the API simple and easy to use.

One of the features of CouchDB is their change notification system. It is explained here. The gist is that there are a couple ways to access change notifications, but they all use HTTP. After reading this I thought, it would be cool to use WebSockets to push these notifications to clients. It's so simple that in less than a half hour I was able to use Node.js and Socket.io to make the worlds worst chat room.

Before we get into it take a look at this short demo of the app I created

It turns out it is really easy, be aware this is not the best Node.js code, but it is good enough to illustrate the point.

First we need to require a few things

var ioreq = require('socket.io'),
    http = require('http'),
    fs = require('fs');

Then we need to create an HTTP server to serve our HTML page

var server = http.createServer(function(req, resp) {
    fs.readFile(__dirname + '/index.htm', 
        function(err, data) {
            if(err) {
                resp.writeHead(500);
                resp.end('An error occurred!');
            } else {
                resp.writeHead(200);
                resp.end(data);
            }
        }
    );
});

Once we have that we can move on to the good stuff

var io = ioreq.listen(server);
io.sockets.on('connection', function (socket) {
    var client = http.createClient(5984, 'localhost');
    var req = client.request('/test/_changes?feed=continuous');
    req.end();
    req.on('response', function(response) {
        response.setEncoding('ascii');
        response.on('data', function(chunk) {
            var update = JSON.parse(chunk);
            if(update.id) {
                console.log('has id ' + update.id);
                var getDocClient = http.createClient(5984, 'localhost');
                var docReq = getDocClient.request('/test/' + update.id);
                docReq.end();
                docReq.on('response', function(docResp) {
                    docResp.setEncoding('ascii');
                    docResp.on('data', function (doc) {
                        socket.emit('update', doc);     
                    });
                });
            }

        });
    });

});

This block of code does a few things. First it creates a client to connect to our CouchDB server and listen for changes. I am using the Continuous feed method, which means I open one HTTP connection and it will stay open and receive data whenever there is a change. This code is not as robust as it could be, in a production system you would probably want to use the heartbeat feature, which will tell CouchDB to send newline characters on an interval so that even if no changes are happening you can still be sure your connection is good. Obviously the above code could be modified to check for the heartbeat, and if it fails restart the connection.

Once the code above gets a change notification it then queries the CouchDB to pull the latest version of the document. Then it emits that change to all the listening WebSocket clients.

We also need a little client side code:

var socket = io.connect('http://localhost:8080');
socket.on('update', function (data) {
    var d = JSON.parse(data);
    if(d.myField) {
        var p = document.createElement('p');
        var cont = document.createTextNode(d.myField);
        p.appendChild(cont);
        var inner = document.getElementById('innerBox');
        inner.appendChild(p);
    }
});

This is a little JavaScript that receives the notification via WebSocket and then adds the value of the myField property to the page. And there you have it, a terrible chat room. In some future posts I'm going to delve into this a little more, I have some cool ideas of how I want to use this technique.

The full code from this article is available on GitHub.

Links

Thanks for reading and Goodnight.

blog comments powered by Disqus