Let’s Play MEAN, Part VII

Deploying with Heroku


Welcome back to this exploratory dive into the MEAN stack.
We’re building a seed repo we can use for all future MEAN projects.
This isn’t that seinfeld episode where you start at the end;
make sure you’re all caught up with the previous parts!

Table of Contents

  1. Introduction, overview and packages.
  2. Directory structure and Node configuration.
  3. Back-end model and route testing.
  4. Angular Adventure.
  5. Front-end testing
  6. Preprocessing with Gulp
  7. Deploying with Heroku (You are here)

Deployment

Probably about time that we try to get this thing online, right? We’ve created the bare minimum for a functional project with all seed components, and now it’s time to see if they work in a production environment.

Target selection

With Rails, I think most people’s first deployment is via Heroku. Heroku is a platform-as-a-service that, frankly, I’ve grown to dislike. It doesn’t give you persistent access to the filesystem, it’s a major pain for scheduling cron jobs, and it’s hideously expensive if you don’t want to wait 10 seconds for your page to load. When it comes to Rails apps, I’d much rather deploy to my own DigitalOcean or Linode server with Mina or Capistrano, or use a better PaaS like ShellyCloud.

All of that said, Heroku does have a few things going for it. You don’t have to go through the grueling process of setting up your own production server, they have a nifty command-line tool, there’s a ton of resources and support for it, and (most importantly) it’s free.

So, with a bit of regret, we’ll be using Heroku in this walkthrough. Some googling reveals that there’s an NPM package for deploying Node apps to nginx servers, and even a Capistrano recipe. I’ll test these options next time.

Getting Set Up

Newcomers to Heroku will need to create an account and download Heroku Toolbelt, their software bundle that includes their command-line tool. Their Toolbelt site has some pretty straightforward instructions, so let’s just copy those:

$ heroku login
Enter your Heroku credentials.
Email: adam@example.com
Password (typing will be hidden):
Authentication successful.

$ cd ~/work/MEAN_stack_starter
$ heroku create
Creating stark-fog-398... done, stack is cedar-14
http://stark-fog-398.herokuapp.com/ | https://git.heroku.com/stark-fog-398.git
Git remote heroku added

What just happened? Well, after logging in, we created a new Heroku app. It assigns it a random name (which we can change later), and added it as a git remote.

If typing ‘heroku login’ is too overburdening, you can set up SSH keys for automatic authentication. I haven’t bothered, yet, but it’s always a good idea.

What’s the deal with cedar?
Is it a tree, or is it a stack?

Two Seinfeld references in one post? Madness.

We can see that heroku create, by default, uses their Cedar-14 stack. I haven’t looked that much into it, but it’s a pre-bundled stack of technologies that includes Ubuntu 14.04 LTS as the OS, and a bunch of pre-installed programming stuffs. Node.js is included, so this ought to work just fine for us.

Heroku has created their own ecosystem, based on the unix process model, and there’s a lot of new ideas associated with it. If you plan on using Heroku for a while, it’s a worthwhile investment to learn more about it.

Setting up a DB

So far, we’ve just been using a locally-running instance of MongoDB. This isn’t going to fly in production.

There are a few options, but I’ve gone ahead and used MongoLab. MongoLab is a free-to-use cloud-hosted database-as-a-service (yes, I’m getting sick of that terminology too) that plays nicely with Heroku.

They’ve got a pretty extensive tutorial article on setting up MongoLab. Rather than paraphrase that whole article, I’m going to cover the most pertinent details only. If you crave more information, you know where to find it.

First, add the addon:

$ heroku addons:add mongolab
Adding mongolab on pixelplay... done, v18 (free)
Welcome to MongoLab. Your new subscription is being created and
will be available shortly. Please consult the MongoLab Add-on Admin UI to check on its progress.
Use `heroku addons:docs mongolab` to view documentation.

Despite the brevity of this command, it did a whole lot. It created an account with MongoLab for us, and added the URL (which includes our login info) as an environment variable. Which is a nice segue into…

Environment Variables

If you can remember all the way back to Part II, we discussed Node’s process.env, and how it contains every local environment variable. In our server.js, we have the following line:

port = process.env.PORT || 3000;

The idea was that to distinguish between the development computer and the production server, we’d just add the environment variable PORT to our server. Heroku makes it nice and easy to add environment variables:

heroku config:set VARIABLE=value

Let’s set a value for our PORT:

heroku config:set PORT=80 

80 is the standard HTTP port; we want to tell the server to run on this port.

For our database, Heroku just created the environment variable MONGOLAB_URI for our new MongoLab server. If you’re curious, you can output it to the console:

$ heroku config | grep MONGOLAB_URI
MONGOLAB_URI: mongodb://heroku_app00000000:random_password@00000000.mongolab.com:12345/heroku_app00000000

Beautiful. Let’s integrate this into our project; add this to your config/db.js

module.exports = {
url: process.env.MONGOLAB_URI || ‘mongodb://localhost/MEAN_stack’
}

In development, that environment variable doesn’t exist and returns nil, so it instead uses localhost. In production, it switches to use mongolab, and authenticates with the in-variable password. Very succinct.

Pushing code, Heroku-style

Rather than give us specialized deployment commands, Heroku uses the git syntax we know and love. Instead of pushing our master branch to origin (github), we can choose instead to push it to heroku. Thus, the deployment command is as simple as this:

$ git push heroku master

This starts by pushing the code to heroku, and then heroku takes over and runs its internal deploy script; it detects that it’s a Node.js app, installs any NPM modules necessary, runs our bower script to install front-end dependencies, and starts the node server.

For all of my gripes about Heroku, they just saved us a bunch of time.

Hopefully, the end of a very long terminal log looks something like this:

remote: ——-> Compressing… done, 19.3MB
remote: ——-> Launching… done, v19
remote: https://stark-fog-398.herokuapp.com/ deployed to Heroku
remote: Verifying deploy… done.

Sure enough, when we navigate to that URL (and make ourselves a tea while we wait for Heroku to boot up), we’re greeted with our beautiful seed face:

We even get HTTPS for free!

Let’s make a Thing.

Well, nothing has thrown an error, but we haven’t actually tested the database connection yet; nothing on our two templates queries the database.

In a real app, this is where we’d start creating the RESTful CRUD paths for our resource. I’m not going to take it that far — this is the deployment episode, after all — but let’s make it so we can add new Things (new/create), and display them all (index).

Planning it out

Rather than create a separate page for the ‘new’ form, I want to embed the partial in the ‘index’ view, maybe floated off to the right. Our Thing model only has 1 field in the Mongoose Schema, a ‘name’ string. So we want users to enter a new Name, click submit, and have it show up in a list in the index.

The cool thing is, all of our back-end code was built and tested back in Part III. We have the back-end routes for POSTing new Things, we have the model specs done. We even have some of the front-end work done: things.service.js handles all RESTful routes.

I was going to write a new end-to-end spec that tested the creation and indexing of the Things, but I decided against it, because we’re veering into the too-specific; I’ll just end up deleting these tests and starting from scratch, so let’s not bother with them for this.

Index

Let’s start by removing our tagline and replacing it with a list of things:

<!-- components/things/things.index.html -->
<div>
<h1>Things Index!</h1>
<ul>
<li class=”thing” ng-repeat=”t in thing.things”>
{{t.name}}
</li>
</ul>
</div>

Right now, thing.things doesn’t exist, so let’s add it to our Thing controller

function ThingController($scope, thingService) {
var thing = this;
thing.things = thingService.query();
}
ThingController.$inject = [‘$scope’, ‘thingService’];
angular.module(‘mainApp.things’).controller(‘ThingController’, [‘$scope’, ‘thingService’, ThingController]);

We’re injecting the thingService we wrote in Part V to handle all server requests.

Three times — don’t forget one!
Injecting our thingService is not as trivial as it should be; we need to reference it in three places. The initial function definition on line 1, the $inject line towards the bottom, and the dependency-injection line at the bottom.
Additionally, the order matters. We have two arguments to this controller, $scope and thingService, and (by convention) Angular built-in providers go first. The order you choose doesn’t really matter, but it has to be consistent in all 3 places.

Our thingService comes with the built-in query() method, which does exactly what we need for an index: Fetches all records, and returns them in an array. Right now we don’t have any Things, so don’t fret about the empty view.

New

Next up, we need to build the ability to add new Things, and the first thing we need is a form.

Angular gives us the handy-dandy ng-include directive (which frankly seems a little magical to me — I’m used to doing includes on the back-end). Let’s create a new view with our form, and include it in our index:

<!-- components/things/things.index.html -->
<div>
<div ng-include=”’components/things/things.new.html’”></div>

<h1>Things Index!</h1>
<ul>
<li class=”thing” ng-repeat=”t in thing.things”>
{{t.name}}
</li>
</ul>
</div>
<!-- components/things/things.new.html -->
<form method="post" ng-submit="thing.post()">
<div class="new-form">
<h3>Add new Thing</h3>
<input name="name" id="thing_name" type="text" ng-model="thing.new_name" placeholder="Enter Name">
<button>Submit</button>
</div>
</form>
Don’t forget the inside quotes!
A common mistake is to not quote your ng-include paths. Notice that there are single quotes inside the double-quotes for the HTML tag. This is because you need to pass ng-include a string. Without the single quotes, it assumes you’re passing in a variable, or a number, or a function, or whatever.

So, we’re creating a form tag, but the form tag is missing an ‘action’ attribute, and has that mysterious ng-submit directive. It’s another one of those event-handling directives, like ng-click, and it comes with the added benefit of disabling the default behavior. In other words, this form will not do a standard HTTP submission, it’ll just invoke the (currently-nonexistent) thing.post() method.

We’re also assigning a model to the input field, called thing.new_name.

Create

To handle that form submission, we’re moving into ‘create’ territory. Let’s add this method to our Thing controller:

function ThingController($scope, thingService) {
var thing = this;
thing.things = thingService.query();
  this.post = function() {
new_thing = thingService.save({}, {name: thing.new_name},
function() {
thing.things.push(new_thing);
}, function() {
alert("Thing not saved.");
}
);
};

}
ThingController.$inject = [‘$scope’, ‘thingService’];
angular.module(‘mainApp.things’).controller(‘ThingController’, [‘$scope’, ‘thingService’, ThingController]);

When we submit the field, we call again on thingService, this time with the built-in method ‘save’. Save just does a basic POST request.

Weird Arguments
Looking at the $resource docs, here are the arguments supplied to $resource.save:

  • non-GET “class” actions: Resource.action([parameters], postData, [success], [error])

What’s weird to me is that parameters and postData are, in this case, equivalent. The former takes an object that shows up in the server under req.query, and the latter takes an object that shows up in the server under req.body.

At any rate, I’m leaving [parameters] blank by passing in an empty object, and supplying our one parameter to postData. This is how our server was set up to accept data back in Part II.

The third argument is our success callback function. On success, our server responds with the newly-saved object. This object is available through the variable we’ve assigned the promise to, new_thing. So, all we need to do is add new_thing to our list of things, and it’ll show up in the view

Wait, what?
This is some mind-bending stuff. We’re assigning a variable to thingService.save, and that variable is available for use within the success callback?? And it somehow contains our save data? This actually does make sense, but it requires a deeper look.
thingService.save() returns a promise. When the promise is resolved, two things happen. The first is that when you make a reference to the promise, it returns the resolved data instead of the promise itself. In other words, thingService.save() now returns whatever the saved result is.
The second thing that happens is that it invokes the ‘success’ callback. So it assigns the server response to new_thing, and then invokes that success callback, where new_thing is pushed to our things array.

The final argument is an error callback. I’m just throwing up a generic alert for now.

Will it blend?

Ok, we have our 3 RESTful resource actions coded in, let’s see if it works.

Yes, it blends!

Huzzah! It’s not pretty, but it works. When I submit the form, it sends that data to Node, and Node adds that data to the server and responds with that new database object. Angular then takes that data, and adds it to our Things array.

We can tell that the back-end is persisting this data by refreshing the page; if your list is still there, it’s reading it from the database.

I committed these changes, and pushed to Heroku. Sure enough, it even works on the internets! You can play with it here.

Because we’re using MongoLab in production, we can view all of our data in a fancy web-based sandbox thing. Here’s what mine looks like:

Look at all those incredible things.

Well, I think that about wraps it up.

It’s official: With a couple of CRUD actions, this feels like a completed shell. Over the past 14,000 words, we’ve hand-built a fully functional MEAN stack, added a fleshed-out full-stack testing kit, threw in some time-saving bells and whistles with Gulp, and gotten it all online for the world to see.

This has been an awesome experience for me, and I hope you guys have gotten something out of it!

You can view the code for this project over on my GitHub page, and fork it for your next MEAN project =)

I love the idea of doing YouTube-style LPs for coding things. If you guys have any suggestions for stuff you want me to cover, hit me up on twitter. Feedback helps too! Want me to do this in video format? Want more in-depth analytical stuff, or more generalized overview stuff? Let me know!

Show your support

Clapping shows how much you appreciated Joshua Comeau’s story.