Introduction

This tutorial is a continuation of the previous tutorial REST Service with Node.js, MongoDB and Express. In this section, we will be adding a web interface to the REST service that we created. Since explanations were already covered for some of the parts in the previous tutorial, we won’t re-visit the explanations here. We will only cover the code that is being changed for this tutorial.

The code for this tutorial is available here on github.

Getting Started

The first step is to setup a new route. If you remember, the previous example you could use the address http://localhost:3000/users to access the REST service, but the root path returned nothing. This time we will add a web page to the root which will be used to manage the “users” REST service.

The following line will be added to the app.js file:

app.get('/', routes.index);

This line adds a route to the node.js application, it uses the path “/”, which means you will hit it when going directly to http://localhost:3000/. We pass as the second parameter routes.index, this tells it to use the file /routes/index.js. Also, we have added a constructor parameter to the UserManager object so that it now takes the app object as a parameter.
Now the whole app.js file looks like this:

var express = require('express')
  , routes = require('./routes')
  , http = require('http')
  , path = require('path');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(require('stylus').middleware(__dirname + '/public'));
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}
var UserManager = require('./userManager').UserManager;
var userManagerService = new UserManager(app);
app.get('/', routes.index);
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

A quick look at the UserManager.js file shows that we have changed the constructor:

UserManager = function(app) {
	var UserProvider = require('./userProvider-mongodb').UserProvider;
	var userProvider = new UserProvider(mongoServer, mongoPort);
	app.set('userProvider', userProvider);

This was so that we could add a parameter to the app object, this parameter is the UserProvider object. We are doing this because we will want to access this object from the index route.

Setting Up The Route

Next we setup the /routes/index.js file, it will look like this:

exports.index = function(req, res){
	var userProvider = req.app.get('userProvider');
	userProvider.fetchAllUsers(function(error, users) {
		if (error) {
			res.send(error, 500);
		} else {
			res.render('index', { title: 'User Manager', users:users });
		}
	});
};

Here we are adding the index route as a function, in that function we get the userProvider from the req.app object, this was the object that was provided by the UserManager object. Here we are showing that you can call this method directly from your route function, this is not technically using the REST service, but I wanted to show that you can use the implementation on the backend as well and to show how the jade template can iterate through results when building the page. For the other management functions we will have the client side of the web page call the REST service instead. Note that we are calling the fetchAllUsers function and if there is no error we call res.render giving it ‘index’ as the first parameter. This first parameter tells it to use the /views/index.jade for rendering. As the second parameter we are passing an object with two properties, one is the title we want to use for the page and the other is the actual list of User objects that was returned by the UserProvider.

Writing the Jade Template Code

Now let’s take a look at the /views/index.jade code:

extends layout
block content
  script(src='http://code.jquery.com/jquery-1.9.1.js')
  script(src='/javascripts/userManager-client.js')
  h1= title
  table
    thead
      tr
        th(class='dataColumn') ID
        th(class='dataColumn') Name
        th(class='dataColumn') City
        th(class='dataColumn') State
        th
    tbody
      - var i=0
      each user in users
        - i++
        tr(class=(i % 2 == 0) ? 'odd' : 'even', id=('id_' + i))
          td #{user._id}
          td
            input(value=(user.name),class='inputField')
          td
            input(value=(user.city),class='inputField')
          td
            input(value=(user.state),class='inputField')
          td
            button(onClick=('onUpdate(' + i + ')')) Update
            button(onClick=('onDelete(' + i + ')')) Delete
      - i++
      tr(class=(i % 2 == 0) ? 'odd' : 'even')
        td
        td
          input(class='inputField',id='txtName')
        td
          input(class='inputField',id='txtCity')
        td
          input(class='inputField',id='txtState')
        td
          button(id='btnAdd',onClick='onAdd()') Add User

There is a lot of new stuff here so take a moment to look through the code and figure out how it works. It is generating HTML, in the “block content” section, the first thing it’s doing is importing two scripts, jQuery and userManager-client.js. After this it goes on to create a table with dynamic contents based on the “users” object passed in. Notice in the tbody section we create a variable using - var i = 0, this is a number we will use to determine odd or even rows. We increment this variable in the each user in users section, which iterates through each of the User objects in the users array giving it the variable name user.

For each row we create a tr element and several td objects. The first td contains the user._id, the next three contain the other fields displayed in an input element so that they can be edited. Finally, two buttons are added, “Update” and “Delete”. These two buttons call javascript functions which are in the userManager-client.js file. At the end of the table we add one more row which contains blank fields and a button for adding a user.

Coding the jQuery AJAX Calls

Now let’s take a look at the /public/javascripts/userManager-client.js file:

function onDelete(id) {
  sel = '#id_' + id;
  userId = $(sel).find('td:eq(0)').text();
  $.ajax({url:'/users/' + userId,type:'DELETE'}).done(function() {
    window.location.href = "/";
  });
}
function onUpdate(id) {
  sel = '#id_' + id;
  user = {};
  user._id = $(sel).find('td:eq(0)').text();
  user.name = $(sel).find('td:eq(1) input').val();
  user.city = $(sel).find('td:eq(2) input').val();
  user.state = $(sel).find('td:eq(3) input').val();
  request = $.ajax({url:'/users/' + user._id,type:'POST', data:user});
  request.done(function() {
    alert('user updated');
    window.location.href = "/";
  });
  request.fail(function(jqXHR, textStatus) {
    alert( "Request failed: " + textStatus );
  });
}
function onAdd() {
  user = {};
  user.name = $('#txtName').val();
  user.city = $('#txtCity').val();
  user.state = $('#txtState').val();
  request = $.ajax({url:'/users', type:'POST', data:user});
  request.done(function(msg) {
    window.location.href = "/";
  });
  request.fail(function(jqXHR, textStatus) {
    alert( "Request failed: " + textStatus );
  });
}

This script has functions for adding, updating and deleting users from the database using the REST service. The functions use jQuery to invoke AJAX calls to the REST service.

Let’s take a quick look at the onDelete function for example. The first line creates a variable sel which has the name of the id_ followed by the id field of the User object. Putting the # tells jQuery to look for an element with that “id” attribute. The second line finds that object by doing $(sel) then uses jQuery’s find method to find the first td element and return the text with the text function.

Now that we have the userId from that td element in the table, we invoke jQuery’s $.ajax function, the first parameter is an object which contains the URL, in this case it will be /users/{userId}, and the HTTP method type, which is “DELETE”. Once this is invoked, it will refresh the page by calling window.location.href = “/”. This is how all of the functions generally work for interacting with the REST service.

Styling The Page With Stylus

Finally, we will style the page so it doesn’t look so bland. To do this we will use the node.js module Stylus. This simply generates CSS code (the generated file is named style.css), the format is almost the same without any curly braces or semicolons and the indentation is required. Let’s take a look at the /public/stylesheets/style.styl file.

body
  padding: 0px
  margin: 0px
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif
  background: #9EA79D
h1
  margin: 0px
  background-color: #B3D8B3
  padding: 5px
  border-bottom: 2px solid
a
  color: #00B7FF
table, td, th
  border: 1px solid #106813
  border-collapse: collapse
  text-align: left
table
  margin: 15px
.dataColumn
  width: 200px
  color: #fff
th
  background-color: #1E3618
.even
  background-color: #E9E5DF
.odd
  background-color: #FFF
.inputField
  width: 200px
#btnAdd
  width: 100%

The stylus code is pretty simple and very easy to understand assuming that you know CSS already.

Go ahead and run the code now and test out the web interface to your REST service. Get the code to run here on github. Again use this command to start the server:

node app.js

Now you will have a web interface which looks something like this:

Each row lists one of the users which have been added to the service. You can change the editable values and click update or you can click delete to delete the user. The last row allows you to enter new fields and click “Add User” to add a new user using the service.

The following two tabs change content below.

Rocky Pulley

Solutions Architect at QAT Global
is a Solutions Architect at QAT Global and has been working as a software development consultant for over 15 years. Most of his recent work has been focusing on Web 2.0 technologies and mobile application development. @rockytriton