Develop easy real-time websites with Meteor

Quickly implement data-driven applications that respond almost instantly to user interaction

Because of the ubiquity of the web, even small delays in the delivery of data can be an irritant to users. They want data to update immediately. Unfortunately, web technology has not quite caught up to this demand for real-time access. While data access is being rapidly standardized into several classes of Object-Relational Mapping (ORM), real-time communication has no such solution. This article discusses Meteor, an exciting new JavaScript framework that aims to solve this problem.

David Berube, Proprietor, Berube Consulting

David Berube photoDavid Berube is a consultant, speaker, and the author of Practical Rails Plugins, Practical Reporting with Ruby and Rails, and Practical Ruby Gems. He has also written for a number of magazines, including Dr. Dobb's Journal and Linux Pro Magazine. He does software development and performance consulting for a variety of clients and industries.



08 January 2013

Also available in Chinese Russian Japanese Vietnamese Spanish

What is Meteor?

Meteor is a new JavaScript framework that intends to automate and simplify the development of web applications that act in real time. It handles real-time communication using a protocol called Distributed Data Protocol (DDP), which is supported on newer browsers using WebSockets and on older browsers using Asynchronous JavaScript + XML (Ajax) long polling. In both cases, the browser-to-server communication is transparent.

The DDP protocol is designed to work with collections of JavaScript Serialized Object Notation (JSON) documents, making it easy to create, update, delete, query, and, of course, view JSON documents. Because DDP is an open source protocol, you can conceivably connect it to any client or data store you please. Out of the box, it works with MongoDB.

In fact, Meteor provides two MongoDB databases: a client-side cache database and a MongoDB database on the server. When a user makes a change to some data—by clicking Save, for example—the JavaScript code running in the browser updates the appropriate database entry in the local MongoDB and then makes a DDP request to the server. The code immediately proceeds as though the operation succeeded because it does not need to wait for the server to reply. Meanwhile, the server updates in the background. If the server operation fails or returns an unexpected result, client-side JavaScript code immediately readjusts according to the newly returned data from the server. This adjustment is called latency compensation and provides an additional amount of perceived speed to the user.

Even Meteor's templating system is explicitly designed to make real-time communication easy. In most web frameworks, you can easily intermingle Hypertext Markup Language (HTML)—or HTML-equivalent markup, such as HTML Abstraction Markup Language (Haml)—with code. This enables you to easily insert dynamic values from the database into pages that are sent to the user. After that, you are responsible for putting a system in place to observe the changes to the data and then update your markup. The templating system in Meteor, however, is designed to record which data is accessed from a template and to automatically set up callbacks to change that HTML when the underlying data changes, making real-time templates easy and quick.


Example: Link popularity contest

Meteor's templating functionality can make a wide variety of real-time applications much easier to write. For example, suppose you want to make a site where users can enter links—that is, uniform resource locators (URLs)—and vote them up or down, with the URLs that are winning the popularity contest displaying at the top of a list. With Meteor, you can easily write such an application in real time so that users can see other users' 65 votes as they make them.


Installing Meteor

To install Meteor, type the code shown in Listing 1 into a Linux® or Mac OS® X terminal. Meteor doesn't support Microsoft® Windows®.

Listing 1. Installing Meteor
curl https://install.meteor.com > install_meteor.sh
chmod u+x install_meteor.sh
./install_meteor.sh

Now you can create a new project.


Creating a new project

The meteor command automates the process of creating a new project with everything Meteor needs to operate. Type the commands shown in Listing 2 to create a project called realtime_links.

Listing 2. Creating your Meteor project
meteor realtime_links
cd realtime_links

Meteor creates a directory that contains an HTML file, a JavaScript file, and a Cascading Style Sheets (CSS) file. The last is a standard CSS file, but the first two are worth discussing. You can download the full versions of both the realtime_links.html and realtime_links.js files from the Download section.


The realtime_links.html file

Listing 3 shows the header and body fragments of the realtime_links.html file.

Listing 3. The realtime_links.html header and body fragments
<head>
<title>Realtime Links Demo</title>
</head>

<body>
  {{> header }}
  {{> link_list }}
  {{> add_new_link }}
</body>

As you can see, the beginning of the HTML template is straightforward. You don't need to worry about including BODY tags, DOCTYPE declarations, or even JavaScript and CSS files. Meteor handles all of that for you. For more information on Meteor's bundling of JavaScript and CSS, see Resources for a link to the Meteor website.

The {{> syntax means "render this template." As you can see, realtime_links.html renders three templates:

  • header is a simple header showing the number of links in the database.
  • link_list shows the list of links and their associated votes.
  • add_new_link is a form for adding new links.

Listing 4 shows the header template.

Listing 4. The realtime_links.html header template
<template name="header">


<h1>The Link Collection</h1>

	<p>We currently have {{collection_size}} links.</p>

</template>

The header template simply renders an h1 tag and a short description of the size of the collection. The collection_size method is defined in the JavaScript file, realtime_links.js (discussed further in the next section). Meteor automatically observes what pieces of data are interpolated by a template. So, when the size of the collection is updated, the header template is automatically updated.

Note that the {{ ... } syntax used here is similar to <%= ... %> in Ruby on Rails or <?= ... ?> in PHP. It can interpolate arbitrary code, so you can insert any useful dynamic expression in that fashion.

Listing 5 shows the link_list template.

Listing 5. The realtime_links.html link_list template
<template name=
"link_list">

  <ul>

    {{#each links }}

      <li>  {{> link_detail }} </li>

    {{/each }}

  </ul>

</template>

As you can see, the code in Listing 5 is a list of links. The links method from the realtime_links.js JavaScript file provides this list. The link_detail template renders for each link. Note that you don't have to pass any arguments because Handlebars' #each loops set the current context for each iteration to be the current object. In other words, local methods of the link_detail template are correctly interpreted as methods of each link object.

Listing 6 shows the link_detail template, which controls the data displayed for each individual link.

Listing 6. The realtime_links.html link_detail template
<template name="link_detail">


<div id="link-{{id}}">

    <h1>{{url}}</h1>

    <p><strong>Stats:</strong> up: {{thumbs_up}} down: {{thumbs_down}} 
net score: {{score}}</p>


<input type="button" value="Thumbs Up" 
 class="thumbs_up" url="{{url}}" />
    <input type="button" value="Thumbs Down" 
class="thumbs_down" url="{{url}}" />


</div>

</template>

The h1 element simply displays the current link's URL. Then there is a short statistics listing that includes the number of times that a link has been voted up, the number of times that it has been voted down, and its net score, which is the difference between the two values. Finally, there are two buttons: one for a thumbs up, or positive vote, and one for a thumbs down, or negative vote. The JavaScript file defines the behavior for these buttons, but there's one more template to examine before diving into that.

Listing 7 shows the add_new_link template.

Listing 7. The realtime_links.html add_new_link template
<template name="add_new_link">

  <div id="new_link_form">

    URL: <input id="url">

<input type="button" value="Click" id="add_url" />

  </div>

</template>

This template is simply a text entry field and a button, which together form the interface for adding new URLs to your list.


The realtime_links.js file

The JavaScript code in realtime_links.js controls the data access and event callbacks from your program, both on the client and on the server. The if (Meteor.is_client) statement marks the client-side part, and the if (Meteor.is_server) statement marks the server part. Meteor provides a way to protect sensitive code so that malicious clients cannot see the source. See the link to the Meteor documentation in Resources for more details.

Listing 8 shows the header and link list helper functions.

Listing 8. The header and link list helper functions
Template.header.collection_size = function () {
return Links.find({}).count();
	};


	Template.link_list.links = function () {
return Links.find({}, {sort : {score: -1} });
	};

The header template uses the first helper function in the listing, which simply returns the size of the links collection. The link_list template uses the second helper function, which returns all the links sorted from the highest score to the lowest score.

Listing 9 has two event callbacks for the link_detail template.

Listing 9. The link_detail event callbacks
	Template.link_detail.events = 
{

'click input.thumbs_up' : function () {
Meteor.call('vote', this.url, 'thumbs_up');
},

  'click input.thumbs_down' : function 
() {Meteor.call('vote', this.url, 'thumbs_down');}

	};

Each event callback handles a vote up or vote down click event. In both cases, they use Meteor.call on the client side to make a function call on the server side. As you can see, making calls from the client to the server is easy. For example, serialization is handled automatically.

Listing 10 shows the event callback for the form where users can add new links.

Listing 10. The add new link form event callback
  Template.add_new_link.events = {

    'click input#add_url' : 
dfunction () {

var new_url = $('#url').val();

      var url_row = Links.findOne( {url:new_url} );

      if(!url_row){

Links.insert( { url : new_url,
score: 0,
thumbs_up: 0,

thumbs_down: 0 });
      }
Meteor.call('vote', url, 'thumbs_up');

    }
  };

First, the form attempts to locate an existing link object with the requested URL. If it finds one, it counts the request as a vote for the existing link object. If it doesn't find one, it creates a new link object and makes a thumbs_up vote for that new object.

This piece of code illustrates both the power and the limitations of Meteor as a cutting-edge technology that is not yet intended for production use. As you can see, the client can simply call insert on the links collection. While this is useful for the developer, it is quite an issue from a security perspective. Fortunately, the developers are actively working on an auth branch of the code that implements powerful authentication features while still maintaining much of the power and flexibility that makes Meteor attractive.

Additionally, realize that Meteor does not currently implement all the MongoDB features. For example, Meteor does not support MongoDB upserts, which insert new data or modify old data. If Meteor supported upserts, you could write the function shown in Listing 11.

Listing 11. A hypothetical add new link form event callback with upserts
  Template.add_new_link.events = {

    'click input#add_url' : function () {

      var new_url = $('#url').val();

       Links.update( { url : new_url}, 
                     { $set: {url : new_url}, 
                       $inc: { votes : 1 } } , true );


    }
  };

As you can see, the code is shorter using upserts. It would probably be faster too because it only requires one round trip to the server. Hopefully, Meteor will implement support for upserts and other new features soon.

The code in Listing 12 runs on the server. It's a single method that can be called by the client-side code. This method, vote, allows the client to vote thumbs_up or thumbs_down on a particular URL. It uses Mongo's $inc operator to increment the appropriate vote counter. It also increments or decrements the total score as necessary. The Meteor.startup method allows code to be run only on server startup. The Meteor.methods function then defines functions that can be called on the client using Meteor.call, as previously shown in Listing 9.

Listing 12. Code for server-side vote method
if (Meteor.is_server) {
    Meteor.startup(function () {
      Meteor.methods({
        vote: function (url, field){

new_obj = { $inc: { } };

 if(field =='thumbs_up'){
new_obj['$inc']['thumbs_up'] = 1;
 new_obj['$inc']['score'] = 1;
 }else{
new_obj['$inc']['thumbs_down'] = 1;
 new_obj['$inc']['score'] = -1;
                }

                Links.update( { url : url}, new_obj );

              }
      });
    });
}

As with Listing 10, you could run the code in Listing 12 on the client. However, for the purposes of illustration, it is run on the server. As Meteor's security model progresses, it's likely that sensitive code will be developed as server-side functions as shown in this listing.


See your application in action

At this point, you can launch your Meteor application, as shown in Listing 13, and see it in action.

Listing 13. Starting your system
meteor

After startup, Meteor runs on port 3000. Open a web browser, and navigate to http://localhost:3000/.

If you enter a URL under Add a URL and click on Add, you should see a URL display with a score of one. You can then click the Thumbs Up or Thumbs Down button to vote for or against the URL. This happens in real time with no page refreshes. If you open a new web browser, you can do the same in another window with the first window instantly reflecting any changes.

When you add multiple URLs, you can see that the one with the highest score—with score being defined as positive minus negative votes—displays at the top. As you vote up or down, URLs move up or down in the list as their rankings change. This happens in real time with multiple users on multiple browsers all receiving the same data as votes are entered.


Conclusion

Meteor is a cutting-edge web framework that contains many interesting concepts. Its support for real-time data is both attractive and important, especially considering that with other technologies real-time support is an afterthought at best. As real-time interaction becomes more important to the future of the web, Meteor's ability to easily and quickly work with complex data sets in real time will continue to become more important as well.


Download

DescriptionNameSize
Example source code for this articlerealtime_links.zip2KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Open source
ArticleID=854090
ArticleTitle=Develop easy real-time websites with Meteor
publish-date=01082013