Prerequisites
To deploy the app to Heroku, you’ll need a Heroku
account. If you have never deployed a Node.js
application to Heroku before, we recommend
going through the Getting Started with Node.js
on Heroku tutorial before you begin.
Also, ensure that you have the following installed
on your local machine:
Heroku toolbelt
Node.js
Source Code Structure
The source code for this project is available on
GitHub at https://github.com/sitepoint-editors/
mean-contactlist. The repository contains:
package.json — a conDguration Dle that
contains metadata about your application.
When this Dle is present in the root directory
of a project, Heroku will use the Node.js
buildpack.
app.json — a manifest format for
describing web apps. It declares environment
variables, add-ons, and other information
required to run an app on Heroku. It is
required to create a “Deploy to Heroku”
button.
server.js — this Dle contains all of our
server-side code, which implements our
REST API. It’s written in Node.js, using the
Express framework and the MongoDB
Node.js driver.
/public directory — this directory contains
all of the client-side Dles which includes the
AngularJS code.
Create a New App
Create a new directory for your app and use the
cd command to navigate to that directory. From
this directory, we’ll create an app on Heroku
which prepares Heroku to receive your source
code. We’ll use the Heroku CLI to get started.
$ git init
Initialized empty Git repository in /
path/.git/
$ heroku create
Creating app... done, stack is cedar-14
https://sleepy-citadel-45065.herokuapp.com/
| https://git.heroku.com/sleepy-
citadel-45065.git
When you create an app, a git remote (called
heroku) is also created and associated with your
local git repository. Heroku also generates a
random name (in this case sleepy-citadel-45065)
for your app.
Heroku recognizes an app as Node.js by the
existence of a package.json Dle in the root
directory. Create a Dle called package.json
and copy the following into it:
{
"name": "MEAN",
"version": "1.0.0",
"description": "A MEAN app that allows
users to manage contact lists",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test
specified\" && exit 1",
"start": "node server.js"
},
"dependencies": {
"body-parser": "^1.13.3",
"express": "^4.13.3",
"mongodb": "^2.1.6"
}
}
The package.json Dle determines the version
of Node.js that will be used to run your
application on Heroku, as well as the
dependencies that should be installed with your
application. When an app is deployed, Heroku
reads this Dle and installs the appropriate Node.js
version together with the dependencies using the
npm install command.
To prepare your system for running the app
locally, run this command in your local directory
to install the dependencies:
$ npm install
After dependencies are installed, you will be
ready to run your app locally.
Provision a MongoDB Database
After you set up your application and Dle
directory, create a MongoDB instance to persist
your application’s data. We’ll use the mLab
hosted database, a fully managed MongoDB
service, to easily provision a new MongoDB
database:
Sign up for a free mLab account.
Create a new single-node Sandbox
MongoDB database in US EAST.
You should now see a mLab Sandbox
database in your account.
Click on the database you just created.
Click the notiDcation telling you to create a
user.
Enter a username and password
When you create a mLab database, you will be
given a MongoDB connection string. This string
contains the credentials to access your database,
so it’s best practice to store the value in a conDg
variable. Let’s go ahead and store the connection
string in a conDg var called MONGODB_URI:
heroku config:set MONGODB_URI=mongodb://
your-user:your-pass@host:port/db-name
Connect MongoDB and the App
Server Using the Node.js Driver
Create a Dle called server.js. In this Dle we’ll
create a new Express application and connect to
our mLab database.
var express = require("express");
var path = require("path");
var bodyParser = require("body-parser");
var mongodb = require("mongodb");
var ObjectID = mongodb.ObjectID;
var CONTACTS_COLLECTION = "contacts";
var app = express();
app.use(express.static(__dirname + "/
public"));
app.use(bodyParser.json());
// Create a database variable outside of the
database connection callback to reuse the
connection pool in your app.
var db;
// Connect to the database before starting
the application server.
mongodb.MongoClient.connect(process.env.MONG
ODB_URI, function (err, database) {
if (err) {
console.log(err);
process.exit(1);
}
// Save database object from the callback
for reuse.
db = database;
console.log("Database connection ready");
// Initialize the app.
var server = app.listen(process.env.PORT
|| 8080, function () {
var port = server.address().port;
console.log("App now running on port",
port);
});
});
Create a RESTful API Server with
Node.js and Express
As our Drst step in creating the API, we deDne the
endpoints (or data) we want to expose. Our
contact list app will allow users to perform CRUD
operations on their contacts.
The endpoints we’ll need are:
/contacts
Method Description
GET Find all contacts
POST Create a new contact
/contacts/:id
Method Description
GET Find a single contact by ID
PUT Update entire contact document
DELETE Delete a contact by ID
Now we’ll add the routes to our server.js Dle:
// CONTACTS API ROUTES BELOW
// Generic error handler used by all
endpoints.
function handleError(res, reason, message,
code) {
console.log("ERROR: " + reason);
res.status(code || 500).json({"error":
message});
}
/* "/contacts"
* GET: finds all contacts
* POST: creates a new contact
*/
app.get("/contacts", function(req, res) {
});
app.post("/contacts", function(req, res) {
});
/* "/contacts/:id"
* GET: find contact by id
* PUT: update contact by id
* DELETE: deletes contact by id
*/
app.get("/contacts/:id", function(req, res)
{
});
app.put("/contacts/:id", function(req, res)
{
});
app.delete("/contacts/:id", function(req,
res) {
});
The code creates a skeleton for all of the API
endpoints deDned above.
Implement the API Endpoints
Next, we’ll add in database logic to properly
implement these endpoints.
We’ll Drst implement the POST endpoint for /
contacts, which will allow us to create and
save new contacts to the database. Each contact
will have the following schema:
{
"_id": <ObjectId>
"firstName": <string>,
"lastName": <string>,
"email": <string>,
"phoneNumbers": {
"mobile": <string>,
"work": <string>
},
"twitterHandle": <string>,
"addresses": {
"home": <string>,
"work": <string>
}
}
The following code implements the /contacts
POST request:
app.post("/contacts", function(req, res) {
var newContact = req.body;
newContact.createDate = new Date();
if (!(req.body.firstName ||
req.body.lastName)) {
handleError(res, "Invalid user input",
"Must provide a first or last name.", 400);
}
db.collection(CONTACTS_COLLECTION).insertOne
(newContact, function(err, doc) {
if (err) {
handleError(res, err.message, "Failed
to create new contact.");
} else {
res.status(201).json(doc.ops[0]);
}
});
});
To test the POST implementation, deploy the
code:
$ git add package.json
$ git add server.js
$ git commit -m 'first commit'
$ git push heroku master
The application is now deployed. Ensure that at
least one instance of the app is running:
$ heroku ps:scale web=1
Then, use cURL to issue a POST request:
curl -H "Content-Type: application/json" -d
'{"firstName":"Chris", "lastName": "Chang",
"email": "support@mlab.com"}' http://your-
app-name.herokuapp.com/contacts
You can visit https://mlab.com/
databases/your-db-name/collections/
contacts and observe your new contact there.
Set up Static Files for the Web App
Now that our API is complete we will use it to
create our web application. The web app allows
users to manage contacts from the browser.
Create a public folder in your project’s root
directory and copy over the Dles from the
example app’s public folder. The folder includes
HTML templates and our AngularJS code.
As you look through the HTML Dles, you might
notice that there’s some unconventional HTML
code, such as “ng-view” in the index.html Dle:
<div class="container" ng-view>
These extensions are features of AngularJS’s
template system. Templates allow us to reuse
code and dynamically generate views for the end
user.
Build the Web App with AngularJS
We’ll use AngularJS to tie everything together.
AngularJS will help us to route user requests,
render different views, and send data to and from
the database.
Our AngularJS code resides in the /public/js
folder in the app.js Dle. To simplify things, we’ll
focus solely on the code that is required to
retrieve and display contacts when the default
homepage route (/) is requested. Implementing
this functionality requires that we:
Render the appropriate view and template
using the AngularJS routeProvider
(index.html and list.html).
Fetch the contacts from the database using
an AngularJS service (GET /contacts).
Pass the data from the service to the view
with an AngularJS controller
(ListController).
The code looks like the following:
angular.module("contactsApp", ['ngRoute'])
.config(function($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "list.html",
controller: "ListController",
resolve: {
contacts: function(Contacts) {
return Contacts.getContacts();
}
}
})
})
.service("Contacts", function($http) {
this.getContacts = function() {
return $http.get("/contacts").
then(function(response) {
return response;
}, function(response) {
alert("Error retrieving
contacts.");
});
}
})
.controller("ListController",
function(contacts, $scope) {
$scope.contacts = contacts.data;
});
Next, we’ll cover each part of the code and what it
does.
Route User Requests with
AngularJS routeProvider
The routeProvider module helps us conDgure
routes in AngularJS.
angular.module("contactsApp", ['ngRoute'])
.config(function($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "list.html",
controller: "ListController",
resolve: {
contacts: function(Contacts) {
return Contacts.getContacts();
}
}
})
})
The homepage route consists of a few
components:
the templateUrl, which speciDes which
template to display
the Contacts service, which requests all of
the contacts from the API server
the ListController, which allows us to
add data to the scope and access it from our
views.
Use AngularJS Services to Make
Requests to the API Server
An AngularJS service generates an object that
can be used by the rest of the application. Our
service acts as the client-side wrapper for all of
our API endpoints.
The homepage route uses the getContacts
function to request the contacts data.
.service("Contacts", function($http) {
this.getContacts = function() {
return $http.get("/contacts").
then(function(response) {
return response;
}, function(response) {
alert("Error retrieving contacts.");
});
}
Our service functions leverage the built-in
AngularJS $http service to generate HTTP
requests. The module also returns a promise,
which you can modify to add additional
functionality (such as logging).
Note that with the $http service we use relative
URL paths (for example, /contacts) as
opposed to absolute paths like app-
name.herokuapp.com/contacts.
Augment Our Scope Using
AngularJS Controllers
So far, we’ve conDgured our route, deDned a
template to display, and retrieved our data using
our Contacts service. To tie everything
together, we’ll create a controller.
.controller("ListController",
function(contacts, $scope) {
$scope.contacts = contacts.data;
})
Our controller adds the contacts data from our
service to the homepage scope as a variable
named contacts. This allows us to access the
data directly from the template (list.html).
We can iterate over the contacts data with
AngularJS’s built-in ngRepeat directive:
<div class="container">
<table class="table table-hover">
<tbody>
<tr ng-repeat="contact in contacts |
orderBy:'lastName'" style="cursor:pointer">
<td>
<a ng-href="#/contact/
{{contact._id}}">{{ contact.firstName }}
{{ contact.lastName }}</a>
</td>
</tr>
</tbody>
</table>
</div>
Completing the Project
Now that we have an understanding of how we
implemented the homepage route in AngularJS,
the implementation for the rest of the web app
routes can be found in the source project’s /
public/js/app.js Ole. They all require a route
deDnition in the routeProvider, one or more
service functions to make the appropriate HTTP
requests, and a controller to augment the scope.
Once you have completed the Angular code,
deploy the app again:
$ git add server.js
$ git add public
$ git commit -m 'second commit'
$ git push heroku master
Now that the web application component is
complete, you can view your app by opening the
website from the CLI:
$ heroku open