Instant web applications with Meteor

Build responsive web applications, from concept to scaled deployment, in record time

With the Meteor web development platform, JavaScript developers can design and build highly interactive and responsive rich-client web applications easily and rapidly. Gain hands-on experience building Meteor apps that take advantage of industry-standard JavaScript libraries. Sing Li goes beyond the hype and explores the promise of Meteor with nontrivial, functional application examples.
Ed note: This tutorial has been updated to reflect changes in the Meteor framework, from early beta in 2013 to the current version, 1.1.x.

Share:

Sing Li, Consultant, Makawave

Author photoSing Li has been a developerWorks author since the site's inception, writing articles and tutorials that cover a variety of web and Java topics. He has more than two decades of system engineering experience, starting with embedded systems, crossing over to scalable enterprise systems, and now back full circle with web-scale mobile-enabled services and "Internet of things" ecosystems.



27 May 2015 (First published 14 June 2013)

Also available in Chinese Russian Japanese

Meteor, a platform for web application development, is gaining significant international adoption. More than a JavaScript coding framework, Meteor provides an innovative way to construct scalable, rich, interactive web applications. Meteor promises to turbocharge the development cycle by simplifying the coding model and reducing the amount of code that developers must write. Using Meteor, experienced web application architects and developers can go from concept to full deployment in weeks, or even days.

For a step-by-step guide on installing and starting to develop with the Meteor platform, follow the Quick Start docs at the Meteor website. In this tutorial, I take you deeper into Meteor development with two detailed application examples and give you an overview of Meteor's approach to web app development. With this knowledge, you'll be able to decide for yourself if rapid web application creation with Meteor suits your needs.

Looking back to the future

The approach that Meteor uses is revolutionary in a sense, yet it has some evolutionary aspects too. It continues along the same IT path as one of the great successes in computing history — spreadsheet software. Figure 1 shows a typical spreadsheet example: a Sales by Region spreadsheet with a pie chart.

Figure 1. The Sales by Regions spreadsheet
Screen capture of a typical spreadsheet showing sales by regions and a pie chart of total sales

Click to see larger image

Figure 1. The Sales by Regions spreadsheet

Screen capture of a typical spreadsheet showing sales by regions and a pie chart of total sales

Trying out the common spreadsheet

You can try out the spreadsheet in Figure 1, which is part of the article's sample code download. Change any of the sales figures, and you'll see the pie chart update automatically.

If you modify any of the regional sales figures in the Sales by Region spreadsheet, the pie chart instantaneously redraws to reflect the new relative proportions of the slices.

This spreadsheet capability is no longer novel, but imagine its impact back in 1983 when Lotus 1-2-3 unveiled the feature. Never before could early PC users do so much work without doing any programming. Spreadsheet software is still one of the killer apps driving PC sales worldwide.

Fast-forward three decades

This tutorial's first web application example — the Sales Portal app shown in Figure 2 — demonstrates how the spreadsheet metaphor has evolved with Meteor, circa 2015.

Figure 2. Sales Portal web application
Screen capture of the Meteor Sales by Region application, showing sales by region and pie chart of total sales

Click to see larger image

Figure 2. Sales Portal web application

Screen capture of the Meteor Sales by Region application, showing sales by region and pie chart of total sales

Sales Portal shows up-to-the-minute regional sales figures and a corresponding pie chart. The global sales director can use the app to monitor sales, and each regional sales team can update its sales figure whenever it makes a sale.

You can download the Sales Portal application and go hands-on with it if you have Meteor installed. Change to the download's sales_nologin subdirectory and type meteor. Point a browser instance at http://localhost:3000/ to display the regional sales figures and pie chart. (If you're unable to install Meteor, you can run the IBM Bluemix hosted version.) Click any sales figure to change it. The pie chart updates immediately after you confirm your change. If you point multiple browser instances at the Sales Portal, all of them update to show the latest sales figures, and you can modify the figures from any one of the browser instances.

Figure 3 shows the US Central team selecting and updating its sales figure.

Figure 3. Updating US Central sales
Screen capture of a user selecting and updating the sales figure for US Central region in the Meteor Sales Portal web application

Click to see larger image

Figure 3. Updating US Central sales

Screen capture of a user selecting and updating the sales figure for US Central region in the Meteor Sales Portal web application

Figure 4 shows the updated US Central sales figure and the up-to-date pie chart. Any user concurrently accessing the Sales Portal at the time of the update sees the change immediately.

Figure 4. Pie-chart proportions updated to reflect new US Central sales figure
Screen capture of the updated pie chart reflecting the new US central sales figure

Click to see larger image

Figure 4. Pie-chart proportions updated to reflect new US Central sales figure

Screen capture of the updated pie chart reflecting the new US central sales figure

Instead of manual updates, you can also imagine a back end where the sales figures consist of subaggregates that are gathered and consolidated autonomously before updating. The visual presentation of the Sales Portal application is identical to that of the venerable spreadsheet, but Sales Portal now also has:

  • Global Internet access via the ubiquitous browser
  • Simultaneous access by multiple users
  • Optional automated back-end data aggregation and consolidation

Significant effort would be required to design, code, and deploy such a system using standard enterprise technology (say, a Java™-based tool chain). Meteor minimizes this effort dramatically, as you'll see in the code walkthrough that follows.

Reactive thinking

This approach greatly reduces the amount of infrastructure code that you need to write, debug, and test.

A key feature of the spreadsheet is its reactive nature. In the Sales by Region example, when a regional sales figure is updated, all other values that depend on that figure are recalculated on the fly. If the dependent component renders graphical output, such as the pie chart, the chart is immediately redrawn with updated slice sizes. You needn't write the code that manages the dependencies (which can be complex) or the code to update the components such as the pie chart. All you do is declare the reactive elements (the sales figures) and their dependencies (the pie chart in this case). The spreadsheet takes care of it all.

Now imagine doing the same today with a web application, and you've got a good picture of how Meteor simplifies web-based system creation.

Meteor reactive defaults

Meteor has certain sources of data that are reactive by default. Most commonly used ones include:

  • Meteor collections — typically, the results of MongoDB queries
  • Variables explicitly bound to Meteor's Session singleton
  • Meteor.user, Meteor.userId, and Meteor.logginIn, which track current user and login status
  • Meteor.status, which tracks server connection status

When you design a Meteor application, you decide on the reactive elements — say, the regional sales data collection. Then, you lay out your presentation layer using standard HTML, CSS, client-side JavaScript libraries and components (such as JQuery, JQuery UI, or JQuery Mobile), and templating technology such as Spacebars (similar in concept to JavaServer Pages but typically runs on the client side). Meteor tracks all dependencies of the reactive elements and then rerenders visual elements and recomputes dependencies to reflect the latest updated values.

This approach greatly reduces the amount of infrastructure code that you need to write, debug, and test. You don't need to write the custom back-end web services to sync the update request, or the code to update the database or data store, or the code to push change notifications out to other connected clients, or the code to fetch updated values from the back end upon notification.


Deep dive into the Sales Portal code

Listing 1 shows the sales.js file, which contains all the server and client-side logic behind the Sales Portal application. This is the only JavaScript code that I needed to write for this application. (You can find sales.js in the sales_nologin directory of the code download.)

Listing 1. Client- and server-side logic for Sales Portal: sales.js
SalesData = new Meteor.Collection("regional_sales");
if (Meteor.isClient) {

    Template.salesdata.helpers({ 
        dataset : function () {
                    return SalesData.find({}, {sort: {row : 1}});
                  }
    });

    Template.datapoint.helpers({ 
        selected :  function () {
                        return Session.equals("selected_datapoint", this._id)
                           ? "selected" : '';
                    }
    });

    Template.title.helpers({
        thisyear:  new Date().getFullYear()
    });

Template.datapoint.events({
        'click': function (event, template) {
          Session.set("selected_datapoint", this._id);
        }
    });

    Template.piechart.helpers({
     'plotchart' : function() {
       plotit(this, SalesData.find({}));
     }
    });

    function plotit(inst,cur)  {

     if (cur.count() === 0)  // do not render pie if no data
           return;
         var data = [];
         cur.forEach( function(sale) {
           data.push( [sale.region, sale.total]);
         });
      window.$.jqplot('chart', [data], 
        { 
          seriesDefaults: {
            // Make this a pie chart.
            renderer: $.jqplot.PieRenderer, 
            rendererOptions: {
              // Put data labels on the pie slices.
              // By default, labels show the percentage of the slice.
              showDataLabels: true
            }
          }, 
          legend: { show:true, location: 'e' }
        }
      );   

    }

    Template.datapoint.onRendered(function()
    {
       this.$('.editable').editable( {
         autotext: 'never',
         type: 'text',
         width: 10,
         mode: 'inline',
         success:    
          if (newvalue != "") {
            var testint = Number(newvalue);
            if (Math.floor(testint) == newvalue) {
           SalesData.update(Session.get("selected_datapoint"), {$set: {total: parseInt(newvalue)}});
          } else {
           return ({newValue: $(this).text()}); 
          }
        } else {
          return ({newValue: $(this).text()});
        },
      display: function() {
       // dummy display function so 'text' is not set, Blaze will set it
      }
      });
});
} // if isClient

if (Meteor.isServer) {
  Meteor.startup(function () {
   if  ((process.env['CLEARDATA']) || (SalesData.find().count() == 0)) {
      var roworder = 0; 
      SalesData.remove({});
      SalesData.insert({region:"US East", total: 2032333, row: roworder++});
      SalesData.insert({region:"US Central", total: 150332, row: roworder++});
      SalesData.insert({region:"US West", total: 1202412, row: roworder++});
      SalesData.insert({region:"Asia Pacific", total: 701223, row: roworder});
  }  
                  
  });

}

The conditionals around the Meteor.isClient and Meteor.isServer variables are runtime context indicators provided by the Meteor core and usable anywhere in your code. In this case, they allow the combination of client and server-side code within the same sales.js file. Any code outside of the conditionals runs on both client and server.

Alternatively, you can separate the client and server source code by placing code served to a client in a subdirectory named client and server-side code in a subdirectory named server. In that scenario you put any assets (such as images) that are served to the client in a subdirectory named public. If you want to restrict certain resources to server access only (not serve them to the client), put them in a subdirectory named private. (The photo-sharing app that you'll work with later in this tutorial uses that directory structure.)

Identifying the reactive data

The selected_datapoint session variable in Listing 1 is reactive. (See the Meteor reactive defaults sidebar for more information on elements that are reactive by default.) The variable is used in this case to change the sales figures' row highlighting. Row highlighting is performed via changes in dynamic CSS styles. The selected_datapoint session variable is updated when a user clicks a row. Because Meteor rerenders dependencies every time this variable changes, the highlight is updated accordingly. Note that selected_datapoint exists only on the client side.

Another reactive source of data for the Sales Portal application is a query on the SalesData Meteor collection. Unlike selected_datapoint, SalesData exists on both client and server; on the server, it's a MongoDB collection. You can see the use of SalesData in Listing 1.

Because the query is reactive, all of its dependencies are recomputed or rerendered when the query result set changes. This is how the sales figures and the pie chart, across all browser instances, get updated. Listing 2 shows the associated HTML template code, found in the sales.html file in the sales_nologin directory of the code download.

Listing 2. Client side HTML and templates: sales.html
<template name="salesdata">
  <div class="salesdata">
    {{#if Template.subscriptionsReady}}
      {{#each dataset}}
        {{> datapoint}}
      {{/each}}
    {{/if}}
  </div>
</template>

<template name="datapoint">
  <div class="datapoint {{selected}}">
    <div id="{{_id}}_region" class="region">{{region}}</div>
    <div id="{{_id}}_total" data-inputclass="inputclass" class="sales editable">{{total}}</div>
  </div>
</template>

The HTML file in Listing 2 is a Spacebars template. Spacebars is Meteor's own templating engine. You can see the Spacebars expressions in Listing 2 inside double curly braces {{ }}. (Through its Blaze rendering system, Meteor can also potentially work with other JavaScript templating engines.)

The rows of sales figures are rendered via the salesdata template code shown in Listing 2. Because this template depends on the dataset helper function, shown in Listing 1, it's rerendered every time the contained reactive query changes.

Seeding sample data on the server

The initial regional sales data for Sales Portal is seeded by the server-side code (from Listing 1) that's shown in Listing 3.

Listing 3. Server-side code to seed data in MongoDB
 SalesData.remove({});
 SalesData.insert({region:"US East", total: 2032333, row: roworder++});
 SalesData.insert({region:"US Central", total: 150332, row: roworder++});
 SalesData.insert({region:"US West", total: 1202412, row: roworder++});
 SalesData.insert({region:"Asia Pacific", total: 701223, row: roworder});

Meteor's latency compensation

Meteor's latency compensation feature is a visual rendition of the eventual consistency concept in big data management. When you update data on the client via the Minimongo emulator, changes are reflected immediately on the client — including reactive rerendering. The changes are also propagated to the server. However, the propagated changes might not succeed for various reasons, including denied access. The publish/subscribe mechanism ensures that the client eventually (usually soon) reflects the server's state. Latency compensation enables a highly responsive UI that is holdup-free — a hallmark trait of modern web applications. The potential cost is a brief moment of visual data inconsistency.

On the Meteor server, a full MongoDB instance is in operation. (This full instance can accept queries and updates from clients other than Meteor.) The same JavaScript MongoDB API is also available on the client side, unifying the client and server coding and enabling code reuse on both client and server. The client-side API is provided by a Mongo emulator called Minimongo. Minimongo uses latency compensation to reflect database changes. Because Minimongo typically deals with small client-side data sets, it doesn't support indexing.

An asynchronous publish/subscribe model is used to control the data that's synchronized between the MongoDB server and the Minimongo client. By default, all server-side Meteor collections are published. Meteor uses Distributed Data Protocol (DDP) to move data between client and server. (DDP drivers in the form of client-side emulator and server-side provider can be created for other databases; ongoing efforts in the Meteor community include a PostgreSQL driver and a RethinkDB driver).

Integrating jQuery plugins

Sales Portal uses the jqPlot jQuery plugin to render the pie chart. The rendering and rerendering of the pie chart is driven reactively by data changes in the SalesData collection. You saw earlier that the salesdata template is rerendered every time the SalesData collection changes. Listing 4 shows the client-side function (from Listing 1) that redraws the pie chart when triggered by the salesdata template's rendered event.

Listing 4. jQuery code to render the pie chart, using the jqPlot plugin
window.$.jqplot('chart', [data], 
     { 
       seriesDefaults: {
         // Make this a pie chart.
         renderer: $.jqplot.PieRenderer, 
         rendererOptions: {
           // Put data labels on the pie slices.
           // By default, labels show the percentage of the slice.
           showDataLabels: true
         }
       }, 
        legend: { show:true, location: 'e' }
     }
   );

Sales Portal uses the x-editable jQuery plugin to enable in-place editing of the sales figures. The code that handles editing appears inside the Template.datapoint.onRendered()handler in Listing 1.


Improving Sales Portal access security

The default prototype mode that Meteor supports is ideal for initial rapid evolution of your applications. During that phase, you are likely modifying the interactions, the UI, and even application logic in rapid iterations — typically without involving any sensitive data. You can share the URL with collaborating developers and reviewing users to gather feedback. But anyone who has the Sales Portal URL can see and even change the sales data. This open-access model is too permissive for production use cases.

The natural next step is to lock the application down by turning on Meteor's security features. The code for a more secure version of the Sales Portal is in the download's sales subdirectory. The added security features are:

Meteor packages

Meteor packages are modules of functionality that you can easily add to or remove from Meteor via the meteor add command. A package can include server and client-side code, stylesheets, UIs, APIs, and more. You see the example of accounts-ui, autopublish, and insecure in this tutorial. But you can also add CoffeeScript support, URI routing, and many more features by adding packages. The Atmosphere repository contains thousands of packages, created by the Meteor community, that you can add to your applications.

  • A user-authentication system to allow only authorized user access to the portal
  • Code that ensures that only an official owner of a region's sales data can modify that data
  • Better source code organization to ensure that potentially secret server-side code is never served to deployed clients

From this point on, all my references to the Sales Portal application are to the secure version that's in the sales directory.

Removing the ability for clients to modify server data

A good start for locking the application down is to prevent anyone from modifying the data. All you need to do in this case is to remove the insecure package with this command:

meteor remove insecure

The insecure package tells the server not to check access rules before reading or changing data. This package is installed by default, allowing all access. After you remove it, no client can modify any server data. If you go back to one of the browser instances and try to modify any of the sales data, you'll notice that the application tries to change the data but quickly reverts, reflecting the denial of access from the server. (This is an example of latency compensation in action. The data is actually updated for a moment in the client, but when the authoritative server copy arrives, it overwrites the client's data.)

After you remove the insecure package, you must add access rules to explicitly allow access to specific pieces of data (by specific users). But you have no users yet. A user database and login-authorization system must be added next.

Ensuring that only authorized users can view the sales data

Before you add the user-authorization system, make sure that nobody can see the sales data. (Authorized users will be allowed to see it later.) Right now, even though they can't modify the data, anyone can view it by going to the Sales Portal URL.

Remove the default autopublish package to prevent any Meteor collection data from being pushed from the server to the client — except data that is explicitly published by the server and subscribed to by the client:

meteor remove autopublish

If you visit the Sales Portal URL now, the regional sales data and pie chart aren't visible.

Adding user login via the accounts package

Meteor has available packages that make adding a user login and authorization system straightforward. The accounts-ui and accounts-password packages cover end-to-end workflow; they include the front-end UI, back-end database, and client-to-server APIs that are required. You can add all of these features to Sales Portal with one command:

meteor add accounts-password accounts-ui

The account-password package supports user creation and login via email-address-plus-password. The implementation uses the Secure Remote Password protocol, and plain-text passwords are never sent between the client and server.

In addition to password-based login, you can also let your users sign in via Facebook, Twitter, Weibo, GitHub, and Google — simply by adding one or more packages to your applications. (Social network OAuth login support is valuable for consumer-facing applications but likely has limited applicability in enterprise intranet environments.)

Customizing user login UIs

If you want further control in styling the dialog boxes used in the UI widget, you can add the accounts-ui-unstyled package instead of the accounts-ui package. If you want to take over the UI completely, consult the Meteor documentation for a description of the API and dataflow used for the process.

Drop-in UI for login

The accounts-ui package provides a prefabricated set of CSS-styled UI widgets (and supporting JavaScript code) to handle user login, new-user creation, and lost-password recovery. To add these widgets, include the {{>loginButtons}} Spacebars template. Listing 5 shows login added to the Sales Portal application in part of the sales/sales.html file.

Listing 5. Adding a user login and authorization system
<body>
<div id="title">
   <div>
        {{> title }}
   </div>
   <div> 
       <div style="float: right">
       {{> loginButtons align="right"}} 
     </div>
   </div>
   </div>
  </div>

The sales directory in the sample-code download contains the completed Sales Portal application with user access control. Run this version (or the Bluemix-hosted version) to try logging in. Start a browser instance, and notice the Sign in link that's now in the top right-hand corner. Click it, and you should see the dialog box shown in Figure 5.

Figure 5. Sign-in dialog box from the accounts-ui package (shown in Firefox)
Browser (Firefox) screen capture of the sign-in dialog box

Click to see larger image

Figure 5. Sign-in dialog box from the accounts-ui package (shown in Firefox)

Browser (Firefox) screen capture of the sign-in dialog box

The accounts-ui package uses Meteor collections and publish/subscribe — the same facilities that are also available for you to use manually in your own code — to implement the user database. In the current version of Sales Portal, I've created two sets of user credentials (email address / password) in the database: joe@dwtestonly.com / abc123 and sing@dwtestonly.com / abc123. Open two browser instances and log in with one credential each.

You can create more users by clicking the Create account link in the Sign in dialog box. Figure 6 shows the create-new-user dialog box, which is a part of the accounts-ui package.

Figure 6. Dialog box for creating new user accounts from the accounts-ui package (shown in Chrome)
Browser screen capture (from Chrome) of the dialog box for creating new users

Click to see larger image

Figure 6. Dialog box for creating new user accounts from the accounts-ui package (shown in Chrome)

Browser screen capture (from Chrome) of the dialog box for creating new users

Adding owner field to regional sales data

In the current version of Sales Portal, the initial database content has been modified. I used the server-side code in Listing 6 to seed the data.

Listing 6. Server-side data-seeding code
SalesData.remove({});
SalesData.insert({region:"US East", total: 2032333, row: roworder++});
SalesData.insert({region:"US Central", total: 150332,  row: roworder++, owner: joeid});
SalesData.insert({region:"US West", total: 1202412, row: roworder++});
SalesData.insert({region:"Asia Pacific", total: 701223, row: roworder});

A new owner field was added in the Listing 6 code. In this case, the owner field contains the user ID of the user (joe@dwtestonly.com) who owns the US Central region data. This field is used to restrict regional sales data updates only to joe@dwtestonly.com. You can obtain user ID values by querying the Meteor.users collection.

Fine-grained selective server-data publishing

With the autopublish package removed, it's necessary to publish data from the server explicitly, and explicitly subscribe to it from the client.

For Sales Portal, the server publishes the regional_sales collection using the code in Listing 7, which is part of the sales/server/sales.js file.

Listing 7. Selectively publishing data from the server
Meteor.publish("regional_sales", function () {
      if (this.userId) {  // only visible to logged in users
      // do not include the owner field for client access
      return SalesData.find({}, {fields: {"region": 1, "total":1 }}); 
      }
});

In Listing 7, note the use of this.userId to ensure that a valid user is logged in to the client-side session. When Meteor runs server code on behalf of a user, this.userId always contains the unique ID of the current logged-in user. If the current browser instance has no logged-in user, this.userId is null, and no data is published. Furthermore, in Listing 7 not all the fields of a regional sales data document (a document being essentially a record with a variable number of fields in a MongoDB instance) are returned in the collection sent to the client. Specifically, the document's owner field is hidden from the client. This is how, using a query, you can publish only a subset of the collection containing a subset of the fields, only to a client with authorized logged-in users. This technique is useful for ensuring that sensitive data fields in certain documents are inaccessible to client browsers.

Client-side data subscription

The Sales Portal client code explicitly subscribes to the server-published regional_sales collection, as shown in Listing 8. Meteor supports subscription on a per-template-instance basis. Using per-template subscription ensures that only data rendered by a template instance is pushed, and that subscriptions are removed correctly when a template instance is destroyed.

Listing 8. Per-template client subscriptions to a collection from server
Template.piechart.onCreated(function() {
  this.subscribe("regional_sales");
});

Template.salesdata.onCreated(function() {
  this.subscribe("regional_sales");
});

Adding access rules to allow regional sales updates

With the insecure package removed, all users are effectively denied from updating the sales data. Assuming the different regional sales figures are owned by different users, an access rule can be added to allow an update of the US Central data by joe@dwtestonly.com. Listing 9 shows this access rule in the server-side source file named model.js.

Listing 9. Server-side access rule allowing owner to update sales figure
SalesData.allow({
  update: function (userId, sales, fields, modifier) {
    if (userId !== sales.owner)
      return false; // not the owner

    var allowed = ["total"];
    if (_.difference(fields, allowed).length)
      return false; // tried to write to forbidden field

    return true;
  },

});

The access-rule function for the update operation returns true if an update is allowed and false if it isn't allowed. The code in Listing 9 checks to make sure that the user is the owner, and that only the total field is being modified.

Meteor hot code reload for iterative development

To save time during development and debugging, keep your browser pointed to the Meteor application even while you are changing code, CSS, or templates. Meteor's hot code reload feature detects the changes and pushes them to the client browsers.

Open Sales Portal and log in as joe@dwtestonly.com. Try to modify the US West data, and notice that you can't. Then try to modify the US Central data. Because joe@dwtestonly.com is the owner of this data, you can update it.

Start another browser instance and log in as sing@dwtestonly.com. Try modifying any of the sales data and notice that you can't. Because sing@dwtestonly.com isn't an owner of any of the sales data, the server denies all modification requests from that user.

You can use the client-side currentUser helper to avoid rendering the templates if the user isn't logged in. Add the code in Listing 10 to the HTML file.

Listing 10. Eliminating attempts to render an empty template
<div id="container">
    <div id="salestable">
      {{#if currentUser}}
          {{> salesdata}}
      {{/if}}
    </div>
   <div>
      {{#if currentUser}}
           {{> piechart}} 
       {{/if}}
        </div>
</div>

Now, start a new Sales Portal browser instance. Note that no data is visible. Log in as sing@dwtestonly.com, and notice that now you can see the data and pie chart. Now try to modify a field; again, you can't modify it because you are not the owner.

Start another browser instance, log in as joe@dwtestonly.com, and note that the data is visible. Modify the US Central figure, and notice that the pie chart updates. Confirm that the pie chart in the sing@dwtestonly.com session has also changed. Sign out of the two sessions and notice that the data now disappears.


Application deployment in the cloud

Deploying Meteor applications to Bluemix is straightforward. Follow the instructions in my screencast:

WATCH:Deploy Meteor apps on Bluemix

You can also use a built-in Meteor command to deploy your application onto Meteor's cloud-hosted server. As of this writing, this service is free of charge. You issue this command from your application directory:

meteor deploy applicationname.meteor.com

The application name must be unique, because it will become available (globally over the Internet) as http://applicationname.meteor.com. You will be prompted to create an account on meteor.com the first time you deploy an app.

At the time of this writing, the Meteor team is working on a new product named Galaxy: a scalable deployment platform based on the Kubernetes orchestration of Docker containers.

To host the application on your own server infrastructure, you need a server with Node.js and MongoDB facilities preinstalled. You can create a deployable bundle of your application using the command:

meteor build  directory to place deployable bundle

Fibers on Node.js

Fibers enables nonpreemptive multitasking among separate logical flows (fibers). A fiber must explicitly yield execution before another fiber can execute. Because you control precisely the point at which execution yields, typically you don't need to protect shared state between fibers. The important feature of fibers is the notational convenience offered: You can write code that looks sequential (such as one thread per logical flow) instead of the deeply nested callbacks typical of Node.js programming.

Because the server-side deployable is a Node.js application, you can customize the deployment topology to your own interoperation or scaling requirements.

The Meteor server-side code runs on Node.js fibers (see the Fibers on Node.js sidebar), providing a virtual environment where you can code as if one thread were handling each incoming request (with no sharable state). This approach can simplify the coding of server-side JavaScript logic.


Foto Share: A mobile photo-sharing service

Now that you've seen where a little design and planning with a bit of Meteor code can take you, you might be thinking up a startup project or two already. The next example will give you more ideas to apply while creating Meteor applications for mobile devices.

Meteor and mobile apps

Meteor has supported the creation of mobile apps for Android and iOS devices since its 1.0 release.

Designing competitive mobile applications requires you to go well beyond the basic ability to compile code that can run on the device (an ability that Meteor's Isobuild tool provides). Good mobile apps must also:

  • Be usable even when not connected to the Internet
  • Not require users to log in every time they access the app
  • Have a look and feel that's native to the device
  • Be ready to be suspended or interrupted at any time, and resumed later
  • Support tight integration with de-facto platform features such as rich notifications, cloud sync, photo gallery, contacts, and social networks

Although these requirements can be addressed via custom code using Apache Cordova's ability to call a device's native APIs, it's beyond the scope of the current Meteor release (and of this tutorial) to cover them. The Foto Share app presented here is a Meteor web app that's usable on mobile devices.

Foto Share is a proof-of-concept web-based photograph-sharing service for mobile phone users. On their phones, users can browse through a collection of their own photos and share photos with their friends via one tap of the Share button. Figure 7 shows Foto Share running on an Apple iPhone.

Figure 7. Foto Share on Apple iPhone
Screen capture of Foto Share on Apple iPhone

Just as in the Sales Portal project, and for the same security reasons, the autopublish and insecure packages have been removed from Foto Share. And to implement password-based login, Foto Share has added the accounts-ui and accounts-password packages. The Foto Share application in the code download has the same two users as the Sales Portal.

To try out Foto Share, first run the application from the fotoshare directory in the code download. (If you don't have Meteor installed, you can run the Bluemix-hosted version.) If you have two phones, point each one's browser to Foto Share. Otherwise, continue to use PC browsers. Sign in as sing@dwtestonly.com on one and joe@dwtestonly.com on the other (using the same password you used for Sales Portal). You can go through Sing's photos by swiping through them, or touch the overlay on either side. Each new photo slides into view. You will discover that all of Sing's pictures are of scenes from Hawaii, and that Joe's are of Aztec and Mayan artifacts.

When you're ready to test the sharing feature, choose a picture in Joe's collection and touch the Share button on the phone. On Sing's phone, Meteor reactively updates the subscribed collection, and Joe's shared picture is now viewable on Sing's phone.

Foto Share user login UI

It would be great to be able to drop in a customizable mobile login UI, as you can for Meteor web applications, but Meteor doesn't (yet) have a mobile friendly accounts-ui package. For Foto Share, I used the accounts-ui web UI. Figure 8 shows the login dialog box as presented on a mobile phone.

Figure 8. Foto Share login screen
Screen capture of the Foto Share login screen on a mobile phone

Identifying reactive data in Foto Share

Conceptually, the most natural data collection to make reactive is the set of user's photos. Doing so would enable Meteor to update and rerender the list of photos whenever a user shares his or her photos. This is the approach taken in this proof of concept. For larger systems, depending on the back-end storage architecture for the images, you might want to make only the photo's metadata reactive, instead of the images themselves.

Take a look at the server-side data-seeding code, shown in Listing 11, to get an idea of how the photos are stored.

Listing 11. Foto Share server-side data-seeding and collection-publishing code
Meteor.startup(function () {
      
      ...     
      
      Fotos.remove({});
      Fotos.insert({name:"pic1", img: readPic('pic1.jpg'),
       owner: sing._id, shared:false});
      Fotos.insert({name:"pic2", img: readPic('pic2.jpg'),
       owner: sing._id, shared:false});
      Fotos.insert({name:"pic3", img: readPic('pic3.jpg'),
       owner: sing._id, shared:false});
      Fotos.insert({name:"pic4", img: readPic('pic4.jpg'),
       owner: joe._id, shared:false});
      Fotos.insert({name:"pic5", img: readPic('pic5.jpg'),
       owner: joe._id, shared:false});
      Fotos.insert({name:"pic6", img: readPic('pic6.jpg'),
       owner: joe._id, shared:false});

    Meteor.publish("photos", function () {
      if (this.userId) {  // only visible to logged in users
       return Fotos.find( {$or : [{owner: this.userId}, {shared: true}]},
  {fields: {"name": 1, "img":1 , "owner": 1}}); 
      }

  });
});

The server-published collection is Fotos. Each document in Fotos representing a photo has name, img, and owner fields. The img field is read in from a corresponding locally stored JPG file. Here again, notice that the data that a subscribing client receives contains only that user's own photos, plus any other photos that are shared by their owner. Here too, selective field filtering is used to remove the shared field from the Foto collections that clients receive. The owner field has not been filtered out, because a client might want to show the name of the owner for a shared picture. Listing 12 shows the readPic() helper function. The function uses Meteor's Assets.getBinary() helper to read the image into memory, and then encode the binary stream into base64 for storage into the img field. This format is convenient for displaying the photograph when it's retrieved on the client side. The Assets package can be used to access resources, available only to the server, under the private subdirectory.

Listing 12. Helper function to read JPG images for MongoDB storage
function readPic(infile)  {       
      var data = Assets.getBinary('images/' + infile);
      var buf =  new Buffer(data);
      var tp = buf.toString('base64');
      return  'data:image/jpeg;base64,' + tp;       
  }

When the images are reconstituted from the database, the template code takes advantage of data URL support in the <IMG> tag on most modern browsers. Data URL support on the SRC attribute of an <IMG> tag enables the dynamic setting of the binary bits of an image through a base64-encoded string. Listing 13 shows the photoitem template. In this template the base64-encoded img field from a photo is used to render the image.

Listing 13. Setting an IMG tag's SRC attribute using data URL
<template name="photoitem">
  <div class="m-item">
       <img id="{{_id}}" src="{{img}}" />
  </div>
</template>

Meteor Remote Methods: Custom RPCs made simple

Even though the insecure package was removed, no access rule has been created in Foto Share. Without access rules, no client can update data via Minimongo. But when the Share button is tapped, the share field of the photo must have been updated somehow. How? The answer is Meteor Remote Methods.

Meteor Remote Methods is a Remote Procedure Call (RPC) mechanism. You can create RPC calls from the client to the server in two steps:

  1. Define a JavaScript function on the server side.
  2. Use Meteor.call() to invoke the server function remotely, optionally passing arguments and callback.

Meteor takes cares of all the endpoint setup, takedown, and data-marshalling chores in between.

When you tap the Share button in Foto Share, the client calls a Meteor Remote Method on the server named shareThisPhoto and passes the photo's ID as an argument. On the server side, the code first checks to see if the caller is the owner of the photo, and it updates the photo's shared field if and only if the owner called the method. Listing 14 shows the server-side shareThisPhoto code.

Listing 14. Meteor Remote Method on the server side to update a photo's shared field
Meteor.methods({
  shareThisPhoto: function (photoId) {
var curPhoto = Fotos.findOne({_id: photoId});
    if (this.userId !== curPhoto.owner)  {
      return "Cannot share this photo.";
    } else {
      Fotos.update({_id: photoId}, {$set :{shared: true}});
      return "Photo shared!";
    }

  },
});

Listing 15 shows the client-side code that invokes the remote method when you tap the Share button.

Listing 15. Client-side code to invoke the remote method when the Share button is tapped
Template.photopage.events({
  'click .fs-logoff': function () {
    Meteor.logout(function() {

      location.reload();
    });
  },
  
  'click .fs-share': function() {
      var curselected = $('.m-active > img').attr('id');

      Meteor.call('shareThisPhoto', curselected, function (error, retval) {
 console.log(retval);
      });
  }
});

I've chosen the RPC approach to demonstrate Meteor Remote Methods. You could instead define an access rule to allow an owner to update the shared field. In that case, you then must locally update the shared field of the photo when the user taps the Share button. Meteor's Minimongo pushes the update to the server, then to all other subscribed clients.


A platform for building modern web applications

Asynchronous data update stream

Subscribed data changes are pushed from the server to the client asynchronously over DDP. Most data sources on the server side are synchronous (such as typical RDBMSs). In the past Meteor used a poll-and-diff algorithm to generate the asynchronous update stream. Current releases of Meteor have more-efficient drivers that parse the MongoDB operations log to generate the stream (known as oplog tailing) where possible. In the near future, Meteor is likely to integrate with the asynchronous feed from native realtime databases such as RethinkDB's change feed.

Now that you've worked through this article's sample applications, it should make sense that Meteor is designed for realtime asynchronous streaming-update web applications.

These types of applications typically consist of a highly interactive single-page UI. Users typically don't experience new page loads; instead, a portion of the displayed page updates instantaneously in response to user interaction (or data changes), with few or no network round-trip delays. The single-page interface in no way restricts the application, because portions of the page can be updated in an infinite variety of ways. This design is reminiscent of a stand-alone desktop application such as a word processor or spreadsheet.

These types of web applications typically load JavaScript application code on the client browser. This code manages the interaction with the user by dynamically manipulating the browser's DOM, modifying CSS styling, generating new HTML elements/code/styling, and using other browser-provided APIs. All interfacing with the user is controlled by the client-side code; no additional HTML or styling is loaded via the network except for the initial application load. The same code also shuttles data back and forth between the client and the server(s) to implement application features. In effect, the browser loads and runs a rich-client (sometimes called fat-client) application written in JavaScript.

On the server side, endpoints are set up to source and sync data from the client securely. Legacy back ends can have RPCs, XML-based web services, RESTful services, or other JSON-style RPC calls. Modern back ends are likely to be streaming data asynchronously from servers via proprietary protocols that are designed to be efficient for the data on the wire, resilient to occasional disconnection, supportive of a variety of current transports, and topologically scalable.


Conclusion

Meteor interview

Learn more about the Meteor project's background and product plans in an interview that Sing Li conducted with Meteor co-founder Matt DeBergalis.

The Meteor team is working to level the playing field for everyone who wants to create single-page, highly interactive, real-time web applications. Most users prefer such applications over the legacy page-at-a-time style. Many of the major web-based mail and office services — including Facebook's Instagram, Google Gmail, Microsoft Hotmail, and Yahoo Mail — use this application architecture. We don't see many instances of these kinds of web applications other than ones developed by the biggest tech companies or most-well-funded startups because they are difficult to build, requiring significant effort and resources. Meteor's stated core mission is to provide a base on which this class of application can be built easily.

Meteor's entrance on the scene makes this a great time to revisit all those long-forgotten application ideas you have stashed away, and see if converting them into a public-facing service might be an appealing venture. If you are already creating or maintaining web applications, Meteor might prove to be the most potent tool in your ever-expanding array of options.


Download

DescriptionNameSize
Sales Portal and Foto Share codewa-meteor-webapps.zip548KB

Resources

Learn

  • Meteor: Explore the Meteor website.
  • Meteor documentation: Check out the official Meteor documentation for the latest features and updates. The Quick Start guide can get you up and running with Meteor in no time.
  • Atmosphere repository: Find thousands of community-contributed, ready-to-integrate packages that you can add to your Meteor apps.
  • Deploy Meteor apps on Bluemix: Deploying meteor apps to Bluemix is straightforward. Follow the directions in this short screencast.
  • MongoDB: The MongoDB NoSQL database stores JSON-style documents and has a succinct query syntax that is used extensively on both client and server Meteor application code.
  • jQuery: Discover everything related to the jQuery framework, including a large list of plugins for different jQuery versions.

Get products and technologies

  • Meteor: Meteor is available for download on GitHub.

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=933895
ArticleTitle=Instant web applications with Meteor
publish-date=05272015