Deliver real-time information to your users using node.js

July 26th 2010

You must have already heard about node.js, if not, it’s an evented input/output server based on Google V8 engine. That means that as soon as the server receives information it can respond and send relevant information back to your front-end. You can then treat this information the way you want. What is so cool about it is that if you know javascript you can code a node.js server and it really is a lot of fun!

In this article, I’ll cover the basics to be able to build a simple server that will respond to an “admin” user input and then broadcast a message to all users connected on the website. Based on that, you’re then free to apply it to whatever needs you may have.

Please note that some basic server knowledge is required, like installing software with command line and forward requests to specific ports.

The demo

First open the live feed demo page, then open the live feed admin page. Try to resize them so you can see both, then click on the display buttons on the admin page, watch the magic happen on the live feed page!

The basics

How does that real-time thing work? Well it’s actually quite simple. Every time a user connects to my demo page, I open an ajax connection, that ajax connection wait for the server to send data. Once it receives data, it’s processed, the connection is closed and a new one is opened. This can be called Comet, long polling, ajax push, HTTP Streaming and a bunch of other names. You can also do long-polling in PHP but since node.js is a dedicated server it does a better job at handling the load.

The setup

If you’ve never installed node.js on your server, you first need to do it. The documentation is here. Make sure you have g++ installed on your server. Simply run: “sudo apt-get install g++” if you’re on Ubuntu, it’ll either tell you it’s up to date or install it.

Here’s how I proceeded to install node.js, 4 easy steps:

[/sources] > git clone git://github.com/ry/node.git
[/sources] > cd node
[/sources/node] > ./configure
...wait...
[/sources/node] > make
...wait...
[/sources/node] > sudo make install
...wait...
'install' finished successfully (0.193s)
DONE

So you should now be setup with node.js on your server, let the fun begin.

The server (link to the source)

This demo uses two main javascript file, one for the server and one for the client. Let’s begin by tearing down the server javascript file.

The first thing you need to do is load the modules required for your app, in this case we need the http, sys, url and querystring module.

var http = require("http"),
    sys = require("sys"),
    url = require("url"),
    qs = require("querystring");

I then declared my global variables for this applications, they are as follow:

var ITEMS_BACKLOG = 20;

var urlMap = {
  '/real_time_feed' : function (req, res) {
    var since = parseInt(qs.parse(url.parse(req.url).query).since, 10);
    feed.query(since, function (data) {
      res.simpleJSON(200, data);
    });
  },
  '/send_feed_item' : function (req, res, json) {
    feed.appendMessage( json );
    res.simpleJSON(200, {});
  }
}

The ITEMS_BACKLOG is the max number of items the server keeps in memory, this way you can make sur it never runs out of memory, you’ll see where it’s being used later.

I then declare my urlMap, these are the URLs the server listens to and the code to execute upon calling. It’s fairly straight forward, there’s one very important thing you need to know tho, it’s that since node.js runs on a different port than your normal webserver (a webserver usually runs on port 80), you need to forward the calls on those URLs to your node.js server port.

In my case, I use NGINX as my webserver and node.js runs on port 8001. Here’s what my NGINX configuration file looks like:

# Send request to node.js on port 8001
location ~* ^(/real_time_feed|/send_feed_item)$
{
  proxy_pass http://localhost:8001;
}

You see that I redirect both URLs in my “urlMap” to the node.js server port. Might not be the most efficient way but since you cannot do cross-domain posting in ajax, it’s the only solution I found for now.

Then comes the meat, the server itself, it’s fairly easy to create, you just need to invoke the “createServer” method and make it listen to a specific port.

http.createServer(function (req, res) {
...
}).listen(8001);

All requests and responses will be processed in here, so you need some code in there right? Here it is:

http.createServer(function (req, res) {
  // Get the url and associate the function to the handler
  // or
  // Trigger the 404
  handler  = urlMap[url.parse(req.url).pathname] || notFound;

  var json = "";

  if(req.method === "POST"){
    // We need to process the post but we need to wait until the request's body is available to get the field/value pairs.
    req.body = '';

    req.addListener('data', function (chunk) {
      // Build the body from the chunks sent in the post.
        req.body = req.body + chunk;
      })
      .addListener('end', function () {
        json = JSON.stringify(qs.parse(req.body));
          handler(req, res, json);
      });
  }else{
    handler(req, res);
  }

  res.simpleJSON = function (code, obj) {
    var body = JSON.stringify(obj);
    res.writeHead(code, {
      "Content-Type": "text/json",
      "Content-Length": body.length
    });
    res.end(body);
  };

}).listen(8001);

So the first thing I do is go get the function that need to be invoked in my urlMap and assign it to my handler. Then depending on the type of request, I either get the posted data and send a response or just straight invoke the handler of the URL called and send a response.

Now you’ll see that in my urlMap I call the feed object, this object has 2 methods, one for querying and one for appending content to the feed.

The “query” method, don’t respond until there’s valid content to send, that’s why the ajax call keeps running, it’s just there waiting. The “appendMessage” method…well she append content to my “real_time_items” array.

// Handles the feed push and querying.
var feed = new function () {
  var real_time_items = [],
      callbacks = [];

  this.appendMessage = function (json) {
    // Append the new item.
    real_time_items.push( json );

    // Log it to the console
    sys.puts(new Date() + ": " + JSON.parse(json).type + " pushed");

    // As soon as something is pushed, call the query callback
    while (callbacks.length > 0)
      callbacks.shift().callback([JSON.parse(json)]);

    // Make sur we don't flood the server
    while (real_time_items.length > ITEMS_BACKLOG)
      real_time_items.shift();
  };

  this.query = function (since, callback) {
    var matching = [];

    for (var i = 0; i < real_time_items.length; i++) {
          var real_time_item = real_time_items[i];
          if (real_time_item.timestamp > since)
            matching.push(real_time_item)
    }

    if (matching.length != 0) {
      callback(matching);
    } else {
      callbacks.push({ timestamp: new Date(), callback: callback });
    }
  };
};

That’s basically it for the server!

The client (link to the source)

What you need to do on the client side is very simple, you only need to start an ajax call that restarts when it receive a response:

function longPoll_feed () {
  //make another request
  $.ajax({
    cache: false,
    dataType: 'json',
    type: "GET",
    url: "/real_time_feed",
    error: function () {
      //don't flood the servers on error, wait 10 seconds before retrying
      setTimeout(longPoll_feed, 10*1000);
    },
    success: function (json) {
      display_event(json);

      //if everything went well, begin another request immediately
      //the server will take a long time to respond
      //how long? well, it will wait until there is another message
      //and then it will return it to us and close the connection.
      //since the connection is closed when we get data, we longPoll again
      longPoll_feed();
    }
});
}

So that ajax call listens to what is sent on the “real_time_feed” url. Once it receives data, it processes them.

Since it’s JSON that is returned to my views, I can then treat it the way I want. This file need to be included in the HTML file that will receive the live content.

The big merge

Ok so now you have your server javascript file and your client javascript file. The first thing you need to do is create an HTML page in which you’ll include the client.js file, I my case, it’s the index.html file.

Now for the server.js file, you actually need to connect via command line and start the node.js server like so:

[scaron] > node /path/to/server.js
Server running at http://127.0.0.1:8001/

And finally, to be able to send content to the server in real time, you need to POST content to the url “/send_feed_item”. If you remember in my URL map, I have setup this url to append content to my feed. So a simple ajax post should do the trick to post content, that’s what I do in my admin page.

$.post(
  'http://nodejs.no-margin-for-errors.com/send_feed_item',
   json,
  function(){
     // Done!
  }
);

The sources

All the sources are available on github, feel free to fork them and play with it yourself.

The possibilities

Let’s just say you watch a live conference on ustream.tv and the speaker mention some website, with node.js he could be able to send you to this website in real time as long as you are connected to his server or you could be watching live news on a website and they mention traffic, you could then be presented a Google traffic map live as they talk about it on the stream.

The possibilites are endless, this post covers only the basics, I’ll let your mind do the rest 😉

131 Responses to “Deliver real-time information to your users using node.js”


  1. こんにちは私の友人)=ありがとうございます!私はこのポストであることを言いたい素晴らしい、良い書かれており、 約すべての重要なに関する情報。 はるかに多くをこのような記事を私が見てみたいと思います。

  2. !私は特定さ かなり 魅力。 ポストが追加された私がどうかを確認するために後で戻って確認します。

  3. 、私私はあなたが私の訪問しました インターネットサイトしたがって、私はのカップル ポイント 問題を見つけます!

  4. 私はあなたのウェブログ発見検索エンジン上に、あなたの初期の記事のいくつかをご確認ください。 非常に良い動作実際にはかなり非常にまで常に保存維持。 I 最近私のMSNニュースリーダーに追加RSSリーダーに増加。 多くの はるかあなたの部分の後に…

  5. ある真この注目すべきテーマに甘いです。 Yと一緒に|私はあなたと沿っ調和します私たち結論と一緒にし、のどが渇いているようにあなた着信アップデートの中に|あなたの内側|} {先にあなたに表示されます。書き込みあなたと一緒に旨のおかげで可能性が高いだけでは驚異的な明瞭にするために、十分なされません。私は意志適切すべての更新を知らさ滞在するあなたのRSSフィードをつかみます。企業の取引あなたと一緒にあなたとの立派に動作し、かなり達成!ない私の本当に 非常に最初舌を| それはそれはだのように、私の下手な英語を許しなさい。

  6. Plunge vegetables into cold water to cook quickly and thoroughly.

    To a large extent this has been the intention and soul purpose of our living.

    ) Manuals for appliances are good sellers as well, so stop and look up those instruction booklets for old ice
    cream makers, crock pots, or stand mixers. By the way, high gloss black
    cast acrylic is quickly becoming one of the most popular colors for kitchen fixture because
    it is easy to clean, easy to maintain, and easy on the
    budget. Having a freezer when no one really uses it is a waste of both money and space.

  7. アントニヌスは、すべてのショーファッショナブルローラーhipkin fianniの李承晩のネプチューンの

  8. 。私は本当にあなたが言っていたものを楽しみました。あなたは間違いなくこの主題に新しい音声をもたらすので、続けます。ない多くの人々はホードが言ったことを言うとまだそれは興味深いものになります。まあ、少なくともイム興味。あなたからこれをはるかに多くを参照するのを待つ傾けます。

  9. 私たちは、たくさんのですボランティアは、私たちのコミュニティにブランドの新しいスキームを開きます。あなたの絵画へ| 情報情報 インターネットサイトは 有益な貴重な参考にを提供してくれました。 あなたがいる可能性があります印象的な仕事をして、私たちのグループ全体が可能性もよい、あなたに感謝します。この 私はなぜdidntは

  10. は私は基本的にこのWebサイトへのバックリンクを組み込む私のブログにこの一部を入れることはできますか?服は人を作り、あなたの服は、搾取工場で行われた場合

  11. ありがとう! データこのために位置長い時間のため、私はすることができませんでした私があった探し信頼できる情報源。

  12. 。私は別のブログ毎日の全く新しい何かを学びます。これは、他の作家からのコンテンツを表示し、そこに少し何かを実践するために刺激されます。私はあなたの心になります私のブログにこのコンテンツの一部を使用します。 Natually病気のサイトに戻ってリンクを作成します。共有を感謝しています。

  13. !私はちょうどあなたが正しいこの投稿への持っている優れた情報のための巨大な親指を放棄したいと思います。私はそうすぐに余分のためにあなたのブログに再び来るということです。こんにちは

  14. 私はビングからあなたのWeb​​ページを発見し、それが優れています。このような有益なポストを提供するためのThankx !!!!ベビーカーベッドには別の日に別の上空で書かれた記事を

  15. できますが、メッセージを私には、私は感謝するだろう。私たちのすべてでこれを共有するあなたは本当にあなたが何について話しているのかを知っているため

  16. 、おかげで、私は今あなたをブックマークします。ポルシェ997ターボと彼のポルシェボクスターを交換したばかりの私の上司を

  17. 「ザナックス(アルプラゾラム)は、不安障害およびパニック発作を治療するために使用されます。アルプラゾラムは

  18. 、これは読み非常に興味深いです。あなたは今までの雑誌に記事を提出する考えがありますか?記事の上ごblog.Theを実現するために、その喜びを

  19. あなたやあなたがゴーストライターを任命でしたが書かれたこれらの記事のすべてがありますか?

  20. ご挨拶! これは本当にある私初期で|を介して| |私は純粋に通っ読書を楽しむ私は素早く叫びを与え、あなたが知っているあなたに伝えたかったので、ここでコメントウェ投稿|あなたののブログを介して|の手段。トピック|あなたはまったく同じ同じを扱う他のブログ/ウェブサイト/フォーラムをお勧めしますか?そうしていただきありがとうございますかなり!プロステッチに任意の名前/番号を使用してカスタマイズ

  21. 私はyouveはいくつかの純粋に興味深いポイントを作ったと思います。ないだけでなく、多くの人々は、実際にはこのことについて、あなただけやったように考えるだろう。イムtheresのは非常に大きく、この主題について明らかにされてthatsの、あなたはとても多くのクラスで、そのように適切にそれをやったことが、本当に感動。 1あなたの良い、男!右ここで本当に素晴らしいもの。ポストのこれらの種類を

  22. はあなたがあなたのブログにこの優れた、Idは感謝して見えただけでどのようにいくつかのアイデアを私にメールしてもよいはずです。読書のための

  23. 真正のトッレSeite。ごみビンeigentlicZufallののhierのgelandet、ABERのICHビンjetztのショーン完全フォン·時間当たりのヌールは途方もないSeiteのbeeindrucktをDER。 Gratuliere大足!ビエールErfolgはdurchデアたsehr gutenホームページ焼きそばフロイントをNOCH。 コンテンツ素材コンテンツのプリティセクションを

  24. マイクロソフトポイントを楽しみにしています。私は再び感謝を言っていた、それはとても良い作ったものと思います!実用的な看護師は、一般的にテキサス&#038で職業看護師を許諾LVNsとして知られているライセンス·看護について

  25. 私はあなたのコンテンツに提供する有用な情報と事実を賞賛します。私はあなたのブログをブックマークし、また私の子供たちは、通常、右ここまで調べてきました。私はtheyllがここで他の誰よりも新しいものをたくさん学ぶことは非常に確信しています!種類のこのようなコンテンツを起動

  26. このウェブサイトはそれで非常に有用なものをたくさん持っています!私を助けてくれてありがとう!

  27. edwall逃れる保持するが、私はこの問題は実際に私は理解しないだろうと思うものであることがわかりました。それはあまりにも複雑で、私にとっては非常に広いと思われます。私はあなたの次のポストのために楽しみにしています、私はそれのこつを取得しよう!

  28. 私はこのウェブサイト上の情報の質に感銘を受けています。良好な資源の多くはここにあります。私はすぐに再びこの場所を訪れると確信しています。非常に有用な情報を

  29. 私はGoogleであなたのブログサイトを発見し、あなたの初期の記事のいくつかを確認します。動作極上を維持し続けています。私のMSN情報リーダーにRSSフィードを私はちょうど余分な。先後にあなたからより多くを勉強しようとしています!

  30. これは私初期私は時間への訪問を支払うここに。私は特定さ たくさんの あなたの内面白いものブログ、特別その議論。だけでなく、 1 持つすべて enjoymen |自分の投稿へのコメントのトンから、私は私は私が推測しますそこ! 仕事 優れたまで| 維持してください。 |と仮定し、あなたの{個人的な収益|個人を| あなたがことがあります可能性がありますとしては問題ありません、それ

  31. はI いないでもはどのように理解 しかし、| 右ここここまで I想定 | 良いかつて|た|公開| |提出|}我慢{にするために使用私は、この後が信じていました。 I しない 認識、あなたがしている| あなたは はずですではありません|あなたはでないために起こる場合|人のために。乾杯!

  32. IVEは数回今、このクールなサイトをチェックし、私は実際にはかなり良い見つけることを言わなければなりません。 youreのがやって何をして続けます!あなたのブログでのもののこのタイプについて話すとき

  33. 私は純粋に 、それは素晴らしい持っている コンテンツいくつかの純粋{素敵

  34. 私はあなたのポストに提示したアイデアのすべてに同意しません。聞きしたいは本当に説得力と確実に動作します。それでも、ポストは初心者のためには短すぎます。あなたは次の時間からそれらを少し拡張していただけますか?ポストをありがとう。

  35. をさようならありがとうございます 優れた 後 他のために。 ここで他にはすることができる 誰その 情報 執筆の方法?このような| の検索を探し 情報 | 上で | 私は私が週、および|プレゼンテーションその後の次の | 私がした私が持っています。

  36. 日は「あなたのイエスはあなたのイエスとするしないし、あなたの何のないあなたのこと」。多数によって読み取られる単語を解除する作家| とのを信じて、信頼関係が整合性に基づいて構築されている に関しては、得られるいくつかのことです。

  37. 私は 今はない 特定 あなたがいる 取得、あなたの情報、しかし 偉大トピック。 学習 | いくつかの時間しばらく過ごす|私は必要がありますする必要がある はるかまたは考え出すより。 謝辞のため素晴らしい 情報 私がした この私の使命のための情報。あなたは実際

  38. 。あなたは全体の記事を読むことがありますか?作家は最後に、あなたのクエリに応答します。

  39. リンジー·ローハンの出廷 – これは人、あなたからの商品悲しい 。あまりにも壮大あなたがかもしれないあなたの前のものと私は実現ました。 I 本当にあなたはここで取得したものを、確かのようなもののようにあなたがしている述べると方法で、これはあなたがそれを言います。賢明なことを| 維持保つ |あなたはそれが面白い作り、あなたはまだそれにもかかわらずの世話をします。リンジー·ローハンの出廷 – これは本当にある 私は遠くを読むのを待つカントもう一度あなたから悲しいです。共有していただきありがとうございます。

  40. これはスマートなブログです。私は真剣です。あなたはこの問題についてあまり知識を持っているし、そんなに情熱。また、応答から明らかに、その背後に人のラリーをするために方法を知っています。ホードは、デザインがここに派手すぎないthatsのが、youreの言うものと同じ大き声明を出すのです。素晴らしい仕事、確かに。このような多くの十分な供給がない

  41. !あなたがそんなにほぼこれ、あなたがそれか何かで電子ブックを書いたように知っているように見えます。私はあなただけのホームメッセージ少しに電力を供給するために、数パーセントで行うことができることを感じるが、それ以外の、それは素晴らしいブログです。幻想的な読み取り。病気は確かに戻って。

  42. は、実際には公開されていることを何楽しみました。それは、神に見捨てられ、インターネット上で私の時間を無駄にしないためのように歓声男本当に(..あなたが知っているREAD、単純に移動する前に、いくつかのuniterestedと肉を食べるゾンビのようにそれを通過していない)を読み取るための素晴らしい記事を見つけるために、その簡単ではありません。 :Pの

  43. 作るはるか来月becuaseクリスマスカードは12月があるalready-

  44. アン非常に 魅力読んで、私は おそらく完全に同意していないが、あなたは、いくつか作るのです非常有効なポイント。

  45. ありがとう。 かなり 興味深い データ。回繰り返してかなりそのため

Leave a Reply