Develop easy real-time websites with Meteor
Quickly implement data-driven applications that respond almost instantly to user interaction
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 Related topics 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 Related topics 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.
Downloadable resources
- PDF of this content
- Example source code for this article (realtime_links.zip | 2KB)
Related topics
- Meteor documentation: Understand all the details about the Meteor application programming interface (API) calls.
- Handlebars documentation: Take a look at the use of the Handlebars templating system used in this article.
- Meteor examples: Explore other examples of small Meteor applications.
- Meteor: An easy-to-use, powerful, real-time web application framework.
- MongoDB: An excellent document-oriented database.
- developerWorks Web development zone: Find articles covering various web-based solutions. See the Web development technical library for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks on Twitter: Join today to follow developerWorks tweets.