Getting started with VMware CloudFoundry, MongoDB and Node.js

Jun 16 • Posted 2 years ago

Listen to the recording of the Node.js Panel Discussion webinar.

Overview

Following up from our previous post we’re posting up a quick how-to for using Node.JS, CloudFoundry and MongoDB together.

Our end goal here is to build a simple web app that records visits and provides a reporting screen for the last 10 visits.

Tools We Need

  1. Sign up for a Cloud Foundry account.
  2. Local installation of MongoDB & Node.JS.
  3. Cloud Foundry VMC tools.
  4. All of the code is available on github.
Follow the links to install & configure the various tools.

Getting Started

  1. Start the mongod process on your local computer. Use the default port.
  2. Confirm that node.js is correctly installed. You should be able to run node from the command-line and receive a basic javascript shell. You should also have NPM (node package manager) installed.
  3. Make a directory for the project and then ensure that CloudFoundry is correctly configured. Mine looked as follows:
<pre > mongo@ubuntu:~$ sudo gem install vmc mongo@ubuntu:~$ vmc target api.cloudfoundry.com Succesfully targeted to [http://api.cloudfoundry.com] mongo@ubuntu:~$ vmc login Email: gates@10gen.com Password: ******** Successfully logged into [http://api.cloudfoundry.com]

Step 1: Hello world

In your directory create a file called app.js. In that file, type the following code. This will create a basic web server on localhost:3000 or on the assigned host:port combination on the CloudFoundry server.

var port = (process.env.VMC_APP_PORT || 3000);
var host = (process.env.VCAP_APP_HOST || 'localhost');
var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(port, host);

Test our file locally:

$ node app.js
$ curl localhost:3000
Hello World
# kill node with CTRL+C

Push our file to CloudFoundry and test. CloudFoundry automatically picks up that we’re using node.js,but it will ask some other configuration questions, including a name and the services we want to have running. I have named mine gvp_node_test and requested that MongoDB be run as a service.

The commands & output:

$ vmc push 
Would you like to deploy from the current directory? [Yn]: Y
Application Name: gvp_node_test
Application Deployed URL: 'gvp_node_test.cloudfoundry.com'? 
Detected a Node.js Application, is this correct? [Yn]: Y
Memory Reservation [Default:64M] (64M, 128M, 256M, 512M, 1G or 2G) 
Creating Application: OK
Would you like to bind any services to 'gvp_node_test'? [yN]: y
The following system services are available::
1. mongodb
2. mysql
3. redis
Please select one you wish to provision: 1
Specify the name of the service [mongodb-55516]: 
Creating Service: OK
Binding Service: OK
Uploading Application:
  Checking for available resources: OK
  Packing application: OK
  Uploading (0K): OK   
Push Status: OK
Staging Application: OK                                                         
Starting Application: OK                                       

At this point you should have a simple working web app. Note the URL: your_app_name.cloudfoundry.com, we can test it is working with curl.

$ curl your_app_name.cloudfoundry.com
Hello World

Step 2: getting mongo configs

CloudFoundry has now configured a MongoDB service with its own user name, password, ip and port. To access these on the server, we will need to parse the environment variables coming into the node process.

To do this we do the following, note that we’ve added an else clause so that we can run this locally.

if(process.env.VCAP_SERVICES){
  var env = JSON.parse(process.env.VCAP_SERVICES);
  var mongo = env['mongodb-1.8'][0]['credentials'];
}
else{
  var mongo = {"hostname":"localhost","port":27017,"username":"",
    "password":"","name":"","db":"db"}
}

The code wraps this up in a generate_mongo_url function that provides a “connection string” of the form mongodb://username:password@host:port/db_name.

Copy in the rest of the code from step 2 on github and test locally.

$ node app.js
$ curl localhost:3000
# connection string for localhost

Once that’s working push the update to the cloud. Notice that we add the name of the project and we don’t get asked any questions:

$ vmc update your_app_name
Uploading Application:
...
Stopping Application: OK
Staging Application: OK                                                         
Starting Application: OK 
# test again
$ curl your_app_name.cloudfoundry.com
# bunch of environment variables

Step 3: now with drivers

First we need to install the node-mongodb-native driver. To do this, we use NPM.

$ npm install mongodb

You should see a new directory at the end of this process: node_modules. To enable use to include these module on the cloud we add this path to the require variable at the top of the code.

require.paths.unshift('./node_modules');

if(process.env.VCAP_SERVICES){
...

Our goal here is to build a function for recording a visit. Let’s build that function.

var record_visit = function(req, res){
  /* Connect to the DB and auth */
  require('mongodb').connect(mongourl, function(err, conn){
    conn.collection('ips', function(err, coll){
      /* Simple object to insert: ip address and date */
      object_to_insert = { 'ip': req.connection.remoteAddress, 'ts': new Date() };

      /* Insert the object then print in response */
      /* Note the _id has been created */
      coll.insert( object_to_insert, {safe:true}, function(err){
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.write(JSON.stringify(object_to_insert));
        res.end('\n');
      });
    });
  });
}

Notice the .connect and .collection('ips'...). We’re telling it to store data in the ips collection.

Another nice feature is the object_to_insert. Saving a document with Node+MongoDB is as simple as creating the object and inserting it.

Let’s fix up the main createServer function.

http.createServer(function (req, res) {
  record_visit(req, res);
}).listen(port, host);

Then we can test locally and push with vmc. If this is working locally, you should be able to connect to your local mongod instance and see some data in the ips collection.

$ mongo localhost:27017/db
> db.ips.find()
...

Step 4

At this point, you’ve probably tested a few times and you’ve successfully put data in the database. Now it’s time to get that data out.

Let’s create a function to print the last 10 visits:

var print_visits = function(req, res){
  /* Connect to the DB and auth */
  require('mongodb').connect(mongourl, function(err, conn){
    conn.collection('ips', function(err, coll){
      /*find with limit:10 & sort */
      coll.find({}, {limit:10, sort:[['_id','desc']]}, function(err, cursor){
        cursor.toArray(function(err, items){
          res.writeHead(200, {'Content-Type': 'text/plain'});
          for(i=0; i < items.length; i++){
            res.write(JSON.stringify(items[i]) + "\n");
          }
          res.end();
        });
      });
    });
  });
}

Let’s update the createServer method to print when we request /history.

http.createServer(function (req, res) {
  params = require('url').parse(req.url);
  if(params.pathname === '/history') {
    print_visits(req, res);
  }
  else{
    record_visit(req, res);
  }
}).listen(port, host);

Again, we test locally and then upload with vmc. If it all works, you should be able to do this:

$ vmc update your_app_name
...
$ curl your_app_name.cloudfoundry.com
{"ip":"172.30.49.42","ts":"2011-06-15T20:14:18.977Z","_id":"4df9129af354f8682d000001"}
$ curl your_app_name.cloudfoundry.com
{"ip":"172.30.49.43","ts":"2011-06-15T20:14:21.745Z","_id":"4df9129df354f8682d000002"}

# now let's test history
$ curl gvp_node_test.cloudfoundry.com/history
{"ip":"172.30.49.43","ts":"2011-06-15T20:14:21.745Z","_id":"4df9129df354f8682d000002"}
{"ip":"172.30.49.42","ts":"2011-06-15T20:14:18.977Z","_id":"4df9129af354f8682d000001"}
...

Going further

  1. Check out our upcoming Node.js Panel Discussion webinar.
  2. For some MongoDB wrappers take a look at
    • Mongoose, an ORM / ODM wrapper
    • MongoSkin, a layer over node-mongodb-native to help reduce callbacks.
  3. For building more complex web sites take a look at the Express framework.

— Gates Voyer-Perrault
@gatesvp