Sunday, August 31, 2014

Spicing up a IBM Bluemix cloud app with MongoDB and NodeExpress

In this post I highlight the rudiments for a creating a cloud application on IBM's PaaS offering Bluemix, using MongoDB and NodeExpress.   Clearly Bluemix allows one to fire up a cloud application with a NoSQL database in a matter of  a few hours which makes it really attractive. The NodeExpress  application was initially created using Enide Studio for Node.js  with a local Mongodb server running on my desktop. (Please see my post Elements of CRUD with Node Express and MongoDB) Once you have ironed out the issues in this local application you are ready to deploy on IBM Bluemix.
The code for this Bluemix application can be forked from bluemix-mongo from IBM Devops.
You can also clone the code from GitHub at bluemix-mongo
Here are the key changes that need to be made for running the NodeExpress Webserver with MongoDB as the backend DB
1) Webserver : Setup the port and host for the Webserver.
  1. app.js
var port = (process.env.VCAP_APP_PORT || 1337);
var host = (process.env.VCAP_APP_HOST || '0.0.0.0');
var app = express();
app.configure(function(){
app.set('port', port);
As seen above the host & port for the Webserver are obtained from the process.env variable.
2) Routes and Middleware
Setup the routes and invoke them appropriately in app.js
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, userlist = require('./routes/userlist')
, newuser = require('./routes/newuser')
, adduser = require('./routes/adduser')
, changeuser = require('./routes/changeuser')
, updateuser = require('./routes/updateuser')
, remuser = require('./routes/remuser')
, deleteuser = require('./routes/deleteuser')
app.get('/users', user.list);
app.get('/helloworld', routes.index);
app.get('/userlist', userlist.list);
app.get('/newuser', newuser.list);
app.post('/adduser',adduser.list);
app.get('/changeuser', changeuser.list);
app.post('/updateuser', updateuser.list);
app.get('/remuser', remuser.list);
app.post('/deleteuser',deleteuser.list);
3) Initialize MongoDB database: Create a set of 3 records when the Webserver starts as follows
if (process.env.VCAP_SERVICES) {
var env = JSON.parse(process.env.VCAP_SERVICES);
if (env['mongodb-2.2']) {
var mongo = env['mongodb-2.2'][0]['credentials'];
}
else {
var mongo = {
"username" : "user1",
"password" : "secret",
"url" : "mongodb://user1:secret@localhost:27017/test"
}
}
var MongoClient = mongodb.MongoClient;
var db= MongoClient.connect(mongo.url, function(err, db) {
if(err) {
log("failed to connect to the database");
else {
log("connected to database");
}
var collection = db.collection('phonebook');
//Clear DB and insert 3 records
remove(mycallback);
var user1 = { "FirstName" : "Tinniam", "LastName" : "Ganesh","Mobile": "916732177728" };
var user2 = { "FirstName" : "Darth", "LastName" : "Vader","Mobile": "6666699999" };
var user3 = { "FirstName" : "Bill", "LastName" : "Shakespeare","Mobile": "8342189991" };
  1. insert(user1,function(err,result){});
  2. insert(user2,function(err,result){});
  3. insert(user3,function(err,result){});
  4. find().toArray(function(err, items) {
});
});
3) Home Page: Setup up a Home page with the CRUD operations when the Bluemix cloud application's route  for e.g. http://bluemix-mongo.mybluemix.net is clicked. This is shown below.
1
 
2
4) Display Users: To display the list of users the route /userlist is invoked. This function gets all the records from the collection and stores them into a toArray element, which is then used for rendering the list of uses with a 'userlist.jade' template
userlist.js
var MongoClient = mongodb.MongoClient;
var db= MongoClient.connect(mongo.url, function(err, db) {
if(err) {
  1. log("Failed to connect to the database");
else {
  1. log("Connected to database");
}
var collection = db.collection('phonebook');
//Get all records and display them
  1. find().toArray(function(err, items) {
  2.    log(items);
  3. render('userlist', {
"userlist" : items
});
});
});
  1. jade
This template displays the list of users as a table. The code is shown below
extends layout
block content
h1= "Display the list of Users"
p
strong Firstname Lastname   Mobile
table
each user, i in userlist
tr
td #{user.FirstName}
td #{user.LastName}
td #{user.Mobile}
p
p
a(href='/') Home
Note: A link back to the Home page is included in here at the bottom.
7
 
5) Adding a User
There are 2 parts to this
a) Invoking the /newuser route to display the input form through the newuser.jade
b) Invoking the /adduser route to insert the values entered in the form. The changes are shown below
a) app.js
..
newuser = require('./routes/newuser')
adduser = require('./routes/adduser')
..
app.get('/newuser', newuser.list);
app.post('/adduser',adduser.list);
b) newuser.js
exports.list = function(req, res){
  1. render('newuser', { title: 'Add User'});
};
The newuser jade displays the input form
c) newuser.jade
extends layout
block content
h1= "Add a User"
form#formAddUser(name="adduser",method="post",action="/adduser")
input#inputUserFirstName(type="text", placeholder="firstname", name="firstname")
input#inputUserLastName(type="text", placeholder="lastname", name="lastname")
input#inputUserLastName(type="text", placeholder="mobile", name="mobile")
button#btnSubmit(type="submit") submit
p
p
a(href='/') Home
3
d) adduser.js
The adduser.js gets the mongo url from the process.env.VCAP_SERVICES and  setups up the connection to the DB and inserts the values received in the 'newuser.jade' form into the database
exports.list = function(req, res) {
if (process.env.VCAP_SERVICES) {
var env = JSON.parse(process.env.VCAP_SERVICES);
if (env['mongodb-2.2']) {
var mongo = env['mongodb-2.2'][0]['credentials'];
}
else {
var mongo = {
"username" : "user1",
"password" : "secret",
"url" : "mongodb://user1:secret@localhost:27017/test"
}
}
// Set up the DB connection
var MongoClient = mongodb.MongoClient;
var db= MongoClient.connect(mongo.url, function(err, db) {
if(err) {
  1. log("Failed to connect to the database");
else {
  1. log("Connected to database");
}
// Get our form values. These rely on the "name" attributes
var FirstName = req.body.firstname;
var LastName = req.body.lastname;
var Mobile = req.body.mobile;
// Set our collection
var collection = db.collection('phonebook');
// Insert the record into the DB
  1. insert({
"FirstName" : FirstName,
"LastName" : LastName,
"Mobile" : Mobile
}, function (err, doc) {
if (err) {
// If it failed, return error
  1. send("There was a problem adding the information to the database.");
}
else {
// Redirect to userlist - Display users
  1. location("userlist");
// And forward to success page
  1. redirect("userlist");
}
});
});
If the insert is successful the userlist page is displayed with the new user
4
6) Updating a User & Deleting a User: Updating and Deleting users follow the same format as Adding a user.
7) index.jade The Home page is built using index.jade with a hyperlink invoking the route for each database operation
extends layout
block content
h1= title
p Welcome to #{title}
ul
li
a(href='/userlist') Display list of users
li
a(href='/newuser') Add a user
li
a(href='/changeuser') Update a user
li
a(href='/remuser') Delete a user
Tip: "Return of the Jade"i : Getting the jade template right is truly an art as Jade is extremely finicky about spaces, tabs, indents and outdents(???). Creating the Jade template had me run into circles. I found out that you can debug the jade template individually by executing
C:> npm install jade -g"
and then  running
C:> jade

from the command prompt. If the result of the command is "rendered
C: >jade index.jade
rendered index.html
8) Push changes to Bluemix: Once the changes have been made push the changes on to Bluemix with 'cf' as follows
cf login -a https://api.ng.bluemix.net
cf push bluemix-mongo -p . -m 512M
cf create-service mongodb 100 mongodb01
cf bind-service bluemix-mongo mongodb01
 
The last 2 commands can also be performed through the Bluemix dashboard in which you add the mongodb service to your Node.js app/
8) Files and Logs: In the Bluemix dashboard you can check your logs in the Files and Logs
5
 
6
Important tip: Finally if the application fails to start when you  push the application with 'cf' for e.g.
cf push -p . -m 512M
....
.....
----> Writing a custom .npmrc to circumvent npm bugs
----> Installing dependencies
----> Caching node_modules directory for future builds
----> Cleaning up node-gyp and npm artifacts
----> No Procfile found; Adding npm start to new Procfile
----> Building runtime environment
----> Checking and configuring service extensions
----> Uploading droplet (7.6M)
of 1 instances running, 1 down
of 1 instances running, 1 down
of 1 instances running, 1 down
of 1 instances running, 1 down

or if  it crashes when you click a link then your debugging friend is
cf logs -- recent
This will dump the error that was encountered either while the application was being started of why the application crashed.
You can fork this Bluemix application from bluemix-mongo at  IBM Devops or from GitHub at bluemix-mongo
Disclaimer: This article represents the author’s viewpoint only and doesn’t necessarily represent IBM’s positions, strategies or opinions
[polldaddy rating="7295683"]
Find me on Google+

Saturday, August 30, 2014

Elements of CRUD with NodeExpress and MongoDB using Enide Studio

In this post I perform basic CRUD operations using NodeExpress and MongoDB with Enide Studio. There is not a whole lot of information in the Web on using Node Express with Enide Studio so the task was kind of difficult. Anyway I managed to get basic CRUD operations to work. The complete code can be cloned from nodeexpress-mongo.
Here it is.
1) To get started create a new Node Express project with Enide Studio File->New->NodeExpress Project.
2) This should create the necessary files, routes and views.
3) Start the MongoDB as follows
mongod --dbpath
4) Start the mongo console. To get started I created 3 records as follows
mongo>
db.phonebook.insert({ "FirstName" : "Tinniam", "LastName" : "Ganesh","Mobile": "916732177728" })
db.phonebook.insert({ "FirstName" : "Darth", "Lastname" : "Vader","Mobile": "6666699999" })
db.phonebook.insert({ "FirstName" : "Bill", "Lastname" : "Shakespeare","Mobile": "8342189991" })
5) You can display the added records with
> db.phonebook.find().pretty()
{
"_id" : ObjectId("53de3f8e0e7f8abf82c0c850"),
"FirstName" : "Tinniam",
"LastName" : "Ganesh",
"Mobile" : "916732177728"
}
{
"_id" : ObjectId("53de3fed0e7f8abf82c0c851"),
"FirstName" : "Darth",
"Lastname" : "Vader",
"Mobile" : "6666699999"
}
{
"_id" : ObjectId("53de40200e7f8abf82c0c852"),
"FirstName" : "Bill",
"Lastname" : "Shakespeare",
"Mobile" : "8342189991"
6) For each of the CRUD operations there are 4 components
- Set the route
- Invoke app.get/app.post as approriate
- Call the necessary route Javascript file
- Use the appropriate Jade constructs for rendering the output
7) Here are the changes for the Display of the users
A) Displaying the User
a) app.js
..
userlist = require('./routes/userlist')
..
app.get('/userlist', userlist.list);
..
var mongodb = require('mongodb');
b) userlist.js
/* GET Phone users page. */
exports.list =  function(req, res) {
// var db = req.db;
var MongoClient = mongodb.MongoClient;
var db= MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {
if(err) {
  1. log("Failed to connect to the database");
else {
  1. log("Connected to database");
}
var collection = db.collection('phonebook');
  1. find().toArray(function(err, items) {
  2.    log(items);
  3. render('userlist', {
"userlist" : items
});
});
});
};
c) userlist.jade
extends layout
block content
h1= "Display the list of Users"
p
strong Firstname Lastname   Mobile
table
each user, i in userlist
tr
td #{user.FirstName}
td #{user.LastName}
td #{user.Mobile}
p
p
a(href='http://localhost:3000') Home
This is shown below
2
 
B) Adding a User
There are 2 parts to this
a) Invoking the /newuser route to display the input form
b) Invoking the /adduser route to insert the values entered in the form. The changes are shown below
a) app.js
..
newuser = require('./routes/newuser')
adduser = require('./routes/adduser')
..
app.get('/newuser', newuser.list);
app.post('/adduser',adduser.list);
These require the following
b) newuser.js
exports.list = function(req, res){
  1. render('newuser', { title: 'Add User'});
};
The newuser jade displays the input form
c) newuser.jade
block content
h1= "Add a User"
form#formAddUser(name="adduser",method="post",action="/adduser")
input#inputUserFirstName(type="text", placeholder="firstname", name="firstname")
input#inputUserLastName(type="text", placeholder="lastname", name="lastname")
input#inputUserLastName(type="text", placeholder="mobile", name="mobile")
button#btnSubmit(type="submit") submit
p
p
a(href='http://localhost:3000') Home
3
The /adduser route inserts the record into the phonebook collection as shown
d) adduser.js
var FirstName = req.body.firstname;
var LastName = req.body.lastname;
var Mobile = req.body.mobile;
// Set our collection
var collection = db.collection('phonebook');
// Insert the record into the DB
  1. insert({
"FirstName" : FirstName,
"LastName" : LastName,
"Mobile" : Mobile
}, function (err, doc) {
if (err) {
// If it failed, return error
  1. send("There was a problem adding the information to the database.");
}
else {
// If it worked, redirect to userlist - Display users
  1. location("userlist");
// And forward to success page
  1. redirect("userlist");
}
});
This takes us  back to Display users if it successfully added the user
4
C) Updating a User and Deleting a User are similar to Adding a User
D) Index page
All the actions are included in the index.jade as shown below with hyperlinks
p Welcome to #{title}
ul
li
a(href='http://localhost:3000/userlist') Display list of users
li
a(href='http://localhost:3000/newuser') Add a user
li
a(href='http://localhost:3000/changeuser') Update a user
li
a(href='http://localhost:3000/remuser') Delete a user
5
Important tip:
Here is an important note. I found that getting the jade template right extremely frustrating. It is really unforgiving and is very picky up indents, outdents(whatever that is!), tabs and spaces. A quick way to check whether your jade template is fine or not is to install jade in your project directory.
npm install jade --global
You can debug your jade with
If there an error you will get
c:> jade userlist.jade
throw e
^
TypeError: userlist.jade:6
4|   h1= title
5|   ul
> 6|      each user, i in userlist
7|         li
8|           #{user.FirstName}
Cannot read property 'length' of undefined
at eval (eval at
If there are no errors you should see that the html is rendered as below
C: >jade index.jade
rendered index.html
You can clone the complete code from GitHub at nodeexpress-mongo