January 10, 2013
Single Page App with Twitter Bootstrap and Node.js

This is Part 1 of a 3 part series on a single page application for Node.JS.

image

I love Twitter Bootstrap. Yes, there are several similar frameworks, but I like the fact that Bootstrap works on nearly all modern browsers (Firefox, Chrome, Safari (Stinkin) IE 8+). Although Twitter Bootstrap was not initially great for Mobile apps, they have done some recent updates that provide near compatibility on mobile devices like iPad, iPhone and Android.

I started this project as a boilerplate for single-page apps, and later realized that it’s a good backend for Bootstrap. There are several great Bootstrap mods, themes and UI layer tools have emerged: Bootswatch, Font Awesome and tons more (thus this amazing list on The Big Badass List of Bootstrap Resources). But, I couldn’t find an adequate starter-kit for the server side so I decided to write my own.

Node.js is a good fit for single-page apps. While Socket.IO would seemingly provide cleaner HTTP better suited to single-page apps, I decided to go with Express 3.0 because I’m more comfortable with it’s session management, templating, form parsing and routing features.

The idea of the single-page app is heavy use of AJAX for the server calls which minimizes full page reloads in the browser. This app is in the long scrolling vertical page style, and I hope later to add a parallax style. Since it is a one page app, it would also be well suited for a tabbed layout which is something I intend to implement later. I’ll get into the details later, but here is a demo the single page application.

image


Part 1: Server-side Code

If you’re new to Node.JS, there is great getting started tutorial here. Once Node and NPM are up and running you’re ready to look at the backend of this single page app. As stated earlier, this app uses the Express framework which is high-performance layer that simplifies app fundamentals such as routing, sessions and request/response.

Express Configuration

app.js

This file uses “require” to load Express, EJS-locals and defines the app settings.

	var express = require('express'),
	engine = require('ejs-locals'),
	app = express();
		
	exports.init = function(port) {

	app.locals({
		_layoutFile:'layout.ejs'
	})
		
	app.configure(function(){
		app.set('views', __dirname + '/views');
		app.set('view engine', 'ejs');
		app.use(express.static(__dirname + '/static'));
		app.use(express.bodyParser());
		app.use(express.methodOverride());
		app.use(express.cookieParser());
		app.use(express.cookieSession({cookie:{path:'/',httpOnly:true,maxAge:null},secret:'skeletor'}));
		app.use(app.router);
		app.enable("jsonp callback");
	});

	app.engine('ejs', engine);
		
	app.configure('development', function(){
	   app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
	});

	app.configure('production', function(){
	   app.use(express.errorHandler()); 
	});

	app.use(function(err, req, res, next){
	   res.render('500.ejs', { locals: { error: err },status: 500 });	
	});
		
	server = app.listen(port);
	console.log("Listening on port %d in %s mode", server.address().port, app.settings.env);

	return app;
	}	

Session and Cookie Management

	.
	.
	app.use(express.cookieParser());
	app.use(express.cookieSession({cookie:{path:'/',httpOnly:true,maxAge:null},secret:'skeletor'}));
	.
	.

Serving Static Pages

	.
	.
	app.use(express.static(__dirname + '/static'));
	.
	.

Routing

server.js

This is the startup file for our app. Server.js loads the app settings from app.js and defines each app route. Express 3.0 is most of the magic here. Express uses app. methods to provide the routing functionality, where is one of the HTTP methods (ie; get, post, del). Below is a stubbed view of server.js that defines each route in the application. Notice the use of “app.get”, “app.post” and “app.del” accordingly:

	var port = process.env.PORT || 4000,
	app = require('./app').init(port),
	dirty = require('dirty');

	// define the routes
	app.get('*', function(req,res,next){
		// this is called for every GET request
	});

	/* home page route */
	app.get('/', function(req,res){
		console.log("render home page");
	});

	/* contact form route */
	app.post('/contact', function(req,res){
		console.log("post from contact form");
	});
		
	/* login routes */
	app.get('/login', function(req,res){
		console.log("render login page");
	});

	app.post('/login', function(req,res){
		console.log("logging in");
	});

	app.get('/logout', function(req,res){
		console.log("logging out");
	});
		
	/* administration routes */
	app.get('/admin', function(req,res){
		console.log("render admin page");
	});

	app.post('/admin/app', function(req,res){
		console.log("saving app settings");
	});

	app.post('/admin/page', function(req,res){
		console.log("saving page settings");
	});

	app.post('/admin/sections/:k', function(req,res){
		console.log("saving section");
	});

	app.del('/admin/sections/:k', function(req,res){
		console.log("deleting section");
	});

	app.post('/admin/sections', function(req,res){
		console.log("saving sections");
	});

	.
	.
	
	/* The 404 Route (ALWAYS Keep this as the last route) */
	app.get('/*', function(req, res){
		res.render('404.ejs', locals);
	});

As you can see the routes define 4 HTTP GET (app.get) endpoints:

  1. / (default/home page)
  2. /login
  3. /logout
  4. /admin

Persistence

For this single-page app I wanted to keep the database lightweight and simple. I chose “Dirty”, a file-based schemaless data store for Node which is ideal for apps with Connect modules. I found Dirty well suited for this app as it eliminates the need to stand-up a separate database server, and keeps deployment stupid simple.


In our server.js we load the node-dirty module using require:

	.
	dirty = require('dirty');
	.
	.

There are 5 database files that provide persistence:

  1. app.db - Stores application level settings
  2. sections.db - Stores section settings and content
  3. user.db - Stores user credentials for /admin authentication
  4. snippet.db - Stores code snippets to pre-populate section content
  5. post.db - Stores results posted from contact and email forms

Code Details

Lets look closer at the code that executes when the default URL (http://www.myapp.com/) is requested:

	app.get('/', function(req,res){
		console.log("show home page");
		
		var appDb = dirty('app.db'),
		sectionsDb;
		
		appDb.on('load', function() {
			sectionsDb = dirty('sections.db');
			sectionsDb.on('load', function() {
if (typeof req.session.once=="undefined") { // once per user session to display message/alert locals.once=appDb.get('page').once; req.session.once=1; } res.render('index.ejs', {locals:locals,sections:sectionsDb,app:appDb.get('app'),page:appDb.get('page'),msg:req.query.msg,err:req.query.err}); }); }); });

The code above provides an example of reading from the ‘dirty’ database, and is fairly self-explanatory. First, the application settings are loaded into variable ‘appDb’. When the appDb ‘load’ event occurs, we get the section content and load it into variable ‘sectionDb’. With node-dirty, we always wait for the 'load' event when reading from the databases, and use '.get' to read a record (based upon its’ unique key). The call to res.render passes the loaded data as a JSON object to the ‘index.ejs’ template file. The JSON object contains the data for ‘sections’ and ‘app’ that can be referenced in the ‘index.ejs’ template (ie. <%= section.title %>). I also use the ‘msg’ and ‘err’ to pass in any notifications that we need to display when ‘index.ejs’ is rendered.

In Part 2, I’ll cover the UI (presentation) portion of the single page app including a detailed look at the EJS templates and Twitter Bootstrap.

Demo | Download

Update! Part 2 - The UI with EJS

  1. vastperhaps reblogged this from in1blog
  2. cliveboulton reblogged this from jalbertbowdenii
  3. jalbertbowdenii reblogged this from in1blog
  4. iatek reblogged this from in1blog
  5. nickleefly reblogged this from in1blog
  6. pixel67 reblogged this from in1blog
  7. in1blog posted this
Follow
in1.com
Twitter: @in1_
Facebook: in1dotcom