Deploy Node.js & MongoDB app to AppFog

I wrote a few articles about various applications before where I was technologies such as Node.js, MongoDB, AngularJS and so on. Some of you have asked for online demos of the actual working apps and I have to admit I was ignoring these requests because I didn't know where to deploy the apps. I know that Amazon gives away an EC2 instance for free, however I am using that for other purposes and I didn't really want to spend money on simple demo apps.

After some research I have come across a PaaS provider called AppFog. (I think this is the right time to point out that I am by no means affiliated with this company, I am using them simply because they suit my needs.)

AppFog, as opposed to a raw EC2, is a managed service. This means that if you're about to deploy a Node.js service, you don't need to install anything, you just start your instance and all the dependencies will already be setup. The downside of this is that unfortunately should you need a different version of Node, you won't be able to do an upgrade.

Within their offerings you can find a free package that comes with:

  • 2 GB free ram (the most appealing offer that I've seen so far)
  • a maximum of 8 service instances (more on this later)
  • 100MB storage per MySQL or PostgreSQL instance
  • 10MB RAM & 6 concurrent connetions for MongoDB (as well as Redis and RabbitMQ)
  • 5GB data transfer per month
  • 100 requets per second

To be fair, if you want to demo your app - this should be more than enough.

After registering you have two options to create your first app - via their GUI online or via a command line tool called af. I'm going to walk you through the latter. My server is a Ubuntu 12.04 LTS - so the instructions below will be relevant to that.

The very first thing to do is to setup af itself. The only requirement that this tool has is version 1.8.7 or higher of Ruby. If you have that, simply execute gem install af.As a next step navigate to the folder that contains your code that you'd like to publish on AppFog and execute af login. This will prompt you for your AppFog username and password - upon entering the right details a success message will appear.

It's time to create an actual application inside AppFog. Execute af push your-app-name. Note that at this point you are giving your application a name and this name will appear under your application lists - which I will show later how to access. At this point af will ask a few questions:

af push airport-api
Would you like to deploy from the current directory? [Yn]: 
Detected a Node.js Application, is this correct? [Yn]: 
1: AWS US East - Virginia
2: AWS EU West - Ireland
3: AWS Asia SE - Singapore
4: HP AZ 2 - Las Vegas
Select Infrastructure: 2
Application Deployed URL [airport-api.eu01.aws.af.cm]: 
Memory reservation (128M, 256M, 512M, 1G, 2G) [64M]: 128M
How many instances? [1]: 

As you can see above, AppFog automatically detect the type of application as well based on the contents of the folder. It also offers you a choice in terms of the instance - I picked an Amazon server in Ireland.

I have deliberatly stopped here for a second as the next question is going to be about services. If you recall, for the free account, you can have up to 8 services. A service can be a MySQL instance, a MongoDB instance and so on. You can only have 8 of these, so if you have 3-4 apps that use MongoDB I'd recommend you setting up one MongoDB service and use different collections to store the data (if you can do this of course). So, with all this being said, let's create a service and assign it to our app - note that if you have setup a service before you'll be asked the first question, otherwise the Create service question will popup:

Bind existing services to 'contact-manager'? [yN]: 
Create services to bind to 'contact-manager'? [yN]: y
1: mongodb
2: mongodb2
3: mysql
4: postgresql
5: rabbitmq
6: redis
What kind of service?: 2
Specify the name of the service [mongodb2-eb085]: 
Create another? [yN]: 
Would you like to save this configuration? [yN]: 
Creating Application: OK
Creating Service [mongodb2-eb085]: OK
Binding Service [mongodb2-eb085]: OK
Uploading Application:
  Checking for available resources: OK
  Processing resources: OK
  Packing application: OK
  Uploading (43K): OK   
Push Status: OK
Staging Application 'airport-api': OK
Starting Application 'airport-api': OK

The difference between the two MongoDB services is that the first one is an older version of MongoDB (v1.8) while the second one is newer (v2.4.8) - the version numbers are applicable at the time of writing.

You may have run into a few error message at this stage, that is normal. First of all, AppFog may complain that you don't have a shrinkwrap file. Simply edit your package.json and add a dependency for the shrinkwrap npm module. Then, either run npm install or npm install shrinkwrap and finally execute npm shrinkwrap. This will create an npm-shrinkwrap.json file.

The second problem that I have come across was that AppFog does not support nodemon - at least I could never make it work. I deployed my app, it came back with some error messages that were realted to nodemon so I have removed this dependency from package.json, removed the appropriate folder from node_modules only to find that the af update command (which is used to push your updates after a code change to AppFog) would fail completely and complain that the node_modules nodemon folder is missing. To overcome this, delete npm-shrinkwrap.json and run npm shrinkwrap again. Retest by executing af update again.

Note: if you'd like to keep on using nodemon you can simply add a .afignore file into your folder with a path leading to the directory that you'd like to exclude when you push your updates to AppFog - it's exactly the same concept as .gitignore.

To see a list of your applications you can use the af apps command which returns with something similar to this:

At this point, your app is in place however the connectivity is still not configure to MongoDB. Luckily this is very straight forward. I am going to show you two examples - the first one comes from the AppFog documentation.

Open the file where you have the MongoDB connectivity setup for your app and add the following above the point where you are about to connect to the database:

if(process.env.VCAP_SERVICES){

    var env = JSON.parse(process.env.VCAP_SERVICES);
    var mongo = env['mongodb2-2.4.8'][0]['credentials'];
}
else{
    var mongo = {
        "hostname":"localhost",
        "port":27017,
        "username":"",
        "password":"",
        "name":"",
        "db":"db"
    }
}
var generate_mongo_url = function(obj){
    obj.hostname = (obj.hostname || 'localhost');
    obj.port = (obj.port || 27017);
    obj.db = (obj.db || 'test');
    if(obj.username && obj.password){
        return "mongodb://" + obj.username + ":" + obj.password + "@" + obj.hostname + ":" + obj.port + "/" + obj.db;
    }
    else{
        return "mongodb://" + obj.hostname + ":" + obj.port + "/" + obj.db;
    }
}
var mongourl = generate_mongo_url(mongo);

var mongoose = require('mongoose');
//For local environment: mongoose.connect(mongourl);

var Schema = mongoose.Schema;

var ContactSchema= new Schema({
    name: { type: String, required: true },
    phone: { type: String },
    email: { type: String },
    facebook: { type: String },
    twitter: { type: String },
    skype: { type: String }
});
//etc etc etc

Note: If you've selected the 'mongodb2' service, use the above code, otherwise for the 'mongodb' service, please use:

var mongo = env['mongodb-1.8'][0]['credentials'];

For another approach have a look at this DB config file on GitHub.

The last thing I'm going to explain in this post is af tunnel. This tool allows you to remotely connect to your service - in our case to MongoDB - and run queries against it. This is extremely useful - it not only allows you to see what data is in your database but also let's you import data.

First of all, install a tool called Caldecott by executing gem install caldecott. (I had an issue where my Ubuntu system told me I need to have Ruby version greater than 1.9.3 in order to install caldecott - which I had. So I updated to 2.0.0 which caused further issues, so I reverted back to 1.9.3 and then caldecott installed without any problems. It still baffles me.)

Once setup, two terminal windows are required. In one the tunnel will be setup and in the second one the actual connectivity to the database.

In the first terminal execute af services which will list all the services against your account. Select one from the provisioned services and run af tunnel [servicename]. You should see something similar:

Then, in your second window, use the username/password combination to connect to the database via port 10000 mongo --port 10000 -u[username] -p[password] db (do not close the first window as that will stop the tunnel):

And this is it. I hope some of you will give AppFog a go, it's a great free service and once you get your head around on how to create an app and how to configure it to work in their environment deploying should become very easy. For further reference please consult their AF CLI reference page.

Also I am giving you a great starting point - if you don't have a Node.js / MongoDB app at the moment, feel free to use the one that I put together - it's available on GitHub. data.json contains all the information about airports, you'll have to import that dataset into MongoDB using mongoimport first - use the above explained tunneling method to access the AppFog MongoDB service.

Happy deploying!

Show Comments