Faye: a Comet client and server for Node.js and Rack

I’m doing my traditional birthday software announcement a little early this year, mostly because I really want to get this out and partly because I’m doing a lot of little bits of work on old projects at the moment and this is the only fancy new thing I’ve got to show.

Spurred on by the sheer volume of awesome emanating from Music Hack Day over the weekend – a few Songkickers went over, I caught the demos via the live stream – I decided to revisit a project I worked on just before the London MHD about six months ago. My hack was this little web service that exposed your iTunes library over HTTP and let you broadcast what you were listening to to other browsers via Comet. The Comet part of it was handled by an early version of Faye, a Ruby server and JavaScript client I cooked up to provide client-to-client messaging.

The Ruby version still exists, and just gained support for Thin’s async response feature, making it totally non-blocking when used behind Thin. The more exciting news is that I’ve also ported the backend to Node.js, a JavaScript environment optimised for non-blocking asynchronous I/O. This makes it much better at serving highly concurrent Comet requests since it uses an event loop rather than blocking threads to respond to each request. It’s a straight port of the Ruby version, which is entirely event-driven except for the web server interface, since Rack expects a synchronous return to respond to requests.

Anyway, onto the code. The backends are trivial to set up, and both currently reside in a single process and keep all channel data in memory. This means you can’t use it behind Passenger (which spawns multiple Ruby processes to serve an app) but I’m hoping some bright spark can help with scaling it. Consider it a fun and reasonably nippy toy right now, I guess. To set up a backend:

// JavaScript
var faye = require('./faye');
var comet = new faye.NodeAdapter({mount: '/comet'});

# Ruby
require 'faye'
comet = Faye::RackAdapter.new(:mount => '/comet')

More complete usage examples are available in the Git repo. Both these backends provide an endpoint at http://0.0.0.0/comet that speaks the Bayeux protocol, and clients can use this to route messages between each other.

Setting up the client is a snap:

<script type="text/javascript" src="/comet.js"></script>
<script type="text/javascript">
  CometClient = new Faye.Client('/comet');
  CometClient.connect();
</script>

Each client (i.e. a JavaScript program running in a browser) can subscribe to named channels and publish arbitrary JavaScript objects to channels. Each message is distributed by the server to all the clients subscribed to that channel, and clients use a callback to handle messages from the channel.

// I can publish a message ...
CometClient.publish('/my/channel', {msg: 'Hello world!'});

// .. and you can pick it up
CometClient.subscribe('/my/channel', function(message) {
  alert(message.msg);
});

Using this it’s absolutely trivial to make simple client-to-client messaging apps – the repo contains the obligatory chat demo, which I’ve modelled on Twitter to illustrate different uses of subscriptions. You don’t need any server-side code for this aside from starting the Faye backend up to handle message routing. The next step would be to provide a client interface on the server side so your backend code can send data out to clients, but we’ll save that for a future release. Contributions in the form of scaling advice and server-side client suggestions would be very much appreciated, in the meantime go have a play with the code.

If you've enjoyed this article, you might enjoy my recently published book JavaScript Testing Recipes. It's full of simple techniques for writing modular, maintainable JavaScript apps in the browser and on the server.