Over the past few weeks I have been teaching myself about Node.js. One of the tutorials I followed was a step by step guide to getting up and running with Node.js, Express, Jade, and MongoDB. After I finished the tutorial and had the app running locally I wanted to get it deployed in the cloud on BlueMix, so I began to investigate what I had to modify in order to do this. After reviewing the code one thing jumped out to me the I knew immediately would be a problem. Want to take a guess at what that is? No? OK, it is the URL to the Mongo database. After completing the tutorial you are left with an app that had a hardcoded DB URL. Obviously deploying the code with the hardcoded URL was not going to work. First before I go into what I had to change, I think a little background is necessary.
When you use a database service in BlueMix, or any Cloud Foundry based PaaS service, the information (credentials, URL, etc) about the database service is passed to the app in the VCAP_SERVICES environment variable. You can read more about services in the BlueMix documentation. The Cloud Foundry documentation also has some good documentation on services.
In the following steps we will be leveraging the Cloud Foundry command line interface (CLI) to deploy our application to BlueMix. You can learn more about the CLI in the documentation here.
OK back to the modifications…
I wanted to make sure I could run the app both locally and have it work in the cloud so I figured a simple if statement would do. Further more the if statement should be able to be based on the presence of the VCAP_SERVICES environment variable. At the top of my app.js file I added a simple if statement and some logging….
console.log('VCAP SERVICES: ' + JSON.stringify(process.env.VCAP_SERVICES, null, 4)); var mongoUrl; if(process && process.env && process.env.VCAP_SERVICES) { var vcapServices = JSON.parse(process.env.VCAP_SERVICES); for (var svcName in vcapServices) { if (svcName.match(/^mongo.*/)) { mongoUrl = vcapServices[svcName][0].credentials.uri; mongoUrl = mongoUrl || vcapServices[svcName][0].credentials.url; break; } } } else { mongoUrl = "localhost:27017/nodetest1"; } console.log('Mongo URL: ' + mongoUrl);
I would like to thank my colleague Pat Mueller for the snippet of code to extract the Mongo DB URL from VCAP_SERVICES. Thanks Pat!
After that you just need to tell monk to use the monogUrl variable.
var db = monk(mongoUrl);
Thats it from a coding perspective, now we need to create an instance of the Mongo DB to use in BlueMix. We can use the cf command line tool for that but first we need to login to BlueMix.
$ cf api https://api.ng.bluemix.net Setting api endpoint to https://api.ng.bluemix.net... OK API endpoint: https://api.ng.bluemix.net (API version: 2.0.0) Not logged in. Use 'cf login' to log in. No org or space targeted, use 'cf target -o ORG -s SPACE' $ cf login API endpoint: https://api.ng.bluemix.net Username> ibmid Password> Authenticating... OK Targeted org rjbaxter@us.ibm.com Targeted space dev API endpoint: https://api.ng.bluemix.net (API version: 2.0.0) User: ibmid Org: ibmid Space: dev
OK, now we are logged in, lets create an instance of the Mongo DB service in BlueMix to use with our app. You can learn more about the Mongo DB service from the documentation.
$ cf create-service mongodb 100 mongodbnode Creating service mongodbnode in org ibmid / space dev as ibmid... OK
The last thing we need to do is create a manifest.yml file in the root directory of our Node JS project that contains deployment information that BlueMix will use to deploy our app in the cloud. In the same folder as your app.js file create a new file called manifest.yml. Inside the manifest.yml copy this code. Then save the file.
applications: - name: nodetest1 memory: 128M command: node app.js services: - mongodbnode
Notice the manifest.yml file is telling BlueMix the name to use for the app, how much memory we need, the command to launch the app, and the name of the Mongo DB service to bind to (the same name we gave the create-service command above).
Now we can deploy the app to BlueMix using the cf push command. Just cd into the directory containing the manifest.yml file and call cf push.
$ cf push Using manifest file /Users/ryanjbaxter/node-examples/nodetest1/manifest.yml Updating app nodetest1 in org ibmid / space dev as ibmid... OK Uploading nodetest1... Uploading from: /Users/ryanjbaxter/node-examples/nodetest1 11.9K, 12 files OK Binding service mongodbnode to nodetest1 in org ibmid / space dev as ibmid OK Starting app nodetest1 in org ibmid / space dev as ibmid... -----> Downloaded app package (8.0K) OK -----> ibm-buildpack-nodejs 2013-01-16 -----> Resolving engine versions WARNING: No version of Node.js specified in package.json, see: https://devcenter.heroku.com/articles/nodejs-versions Using Node.js version: 0.10.21 Using npm version: 1.2.30 -----> Fetching IBM SDK for Node.js binaries -----> Vendoring node into slug -----> Installing dependencies with npm ... Dependencies installed -----> Building runtime environment + '[' -e /tmp/staged/app/ACE_EMPTY_RUNTIME ']' + echo no + exit 1 -----> Uploading droplet (34M) 1 of 1 instances running App started Showing health and status for app nodetest1 in org ibmid / space dev as ibmid... OK requested state: started instances: 1/1 usage: 128M x 1 instances urls: nodetest1.mybluemix.net state since cpu memory disk #0 running 2014-02-23 04:19:40 PM 0.0% 19.3M of 128M 93.5M of 1G
Then if you go to http://nodetest1.mybluemix.net/newuser you can create a new user which gets persisted to the Mongo DB you tied to the app. Thats it, you just deployed the app to the cloud! Pretty easy! Notice you didn’t need to worry about spinning up a VM, setting up Node, or setting up Mongo, all that is done for you, and in a very short period of time. All that time you spent at the beginning of the tutorial setting up Node and Mongo on your local machine was saved when you went to the cloud.
Now for more complex apps there may be more changes you need to make in order to move the app to the cloud. For example, keeping state in the app is generally a bad idea in the cloud if you have multiple instances of the app running. I will address this concern in an upcoming blog post. For now, happy coding!