It has been surprisingly hard to find a very simple tutorial to get started with Express, along with some common helpful tools, including tests!

Here’s a little tutorial for Node.js v0.10 and Express 4. I’m learning Express, since I’m working on an app in SailsJS, so I will pick options that mirror choices made by the SailsJS framework.

Install Express

Express is a popular simple web app framework for Node (similar to Sinatra for Ruby), and is easily instally with the fabulous Node Package Manager, npm. I find the generators to be handy (at least for learning) and don’t ship with Express anymore, so you need to install them separately.

npm install -g express
npm install -g express-generator

Create an Express App

Let’s create an app named ‘test-app’ — this will create a new directory of that name with all the app files in it.

express test-app -e

The -e option tells express-generator to use ejs. (From the Express guide: Jade is the default. Express-generator supports only a few template engines, whereas Express itself supports virtually any template engine built for node. For the complete list, see express --help

It shows you all the files it creates and even gives a hint about next steps:

cd test-app
npm install

npm install will download all of the dependencies specified in our “package.json” file and put them in the the node_modules directory. This directory will get big fast, so we probably want to add to .gitignore.

Run the App!

Start the server

npm start

Then go to http://localhost:3000/ and see:
Browser with URL http://localhost:3000 shows Express in large letters, smaller letters below display "Welcome to Express"

Stop the server with ctrl-C.

Take a moment to review the contents of the generated package.json, the npm docs are a good reference for the defaults. All of the dependencies that we have right now are ones that express decided we should have. Max Ogden has some nice docs about Node modules.

Add a “devDependency” section to package.json:

  "devDependencies": {
    "mocha": "*",
    "chai": "*",
    "supertest": "*",
    "supervisor": "*"

We’re adding a set of tools that are installed with npm but only used for development and testing.

Don’t forget to add a comma or we’ll get a scary looking error:

npm ERR! install Couldn't read dependencies
npm ERR! Failed to parse json
npm ERR! Unexpected string

Also in “package.json,” change

  "scripts": {
    "start": "node ./bin/www"


  "scripts": {
    "start": "supervisor ./bin/www",
    "test": "./node_modules/.bin/mocha"

the install the new packages with:

npm install

We’ve just added a set of development tools for rapid iteration and testing. The scripts section lets us create shortcuts for the npm command.

Supervisor Allows Fast Experimentation

Supervisor makes it so you can edit files and just refresh the page to see the change. Now that we have edited the npm ‘start’ script to use supervisor, we can:

npm start

We can view the main index page, by going to http://localhost:3000. Then without stopping the server, let’s edit


so the H1 text says “Hello!” instead of Express. We can refresh the page to see the update.

Mocha, Chai and Supertest for Testing

Mocha will serially run a set of tests and report failures nicely. It supports a number of different assertion libraries. Chai is a single assertion library that supports the popular variants: assert, expect and should.

You’ll need to create a test directory, empty for now. Let’s make sure we’re set up right:

mkdir test
npm test

We should see:

  0 passing (2ms)

create the file


with a test

var request = require('supertest')
  , express = require('express');
var app = require('../app');
describe('Index Page', function() {
  it("renders successfully", function(done) {
    request(app).get('/').expect(200, done);    

run the test with

npm test

and it passes!

Adding New Behavior Test First

We can add expectations to our test. Let’s plan to add the text “Hello World to the index page. Supertest supports simple regex syntax for comparing text. The super test API cleverly supports concise testing by assuming a number as the first param is a status code, but a regex or a string wants to compare to the body.

  it("renders successfully", function(done) {
      .expect(/Hello World/, done);    

This will fail

  1) Index Page renders successfully:
     Error: expected body '\n\n  \n    Express\n    \n  \n  \n    <h1>Hello!</h1>\n    

Welcome to Express

\n \n\n' to match /Hello World/

Now we can edit the page in


and run the test again with

npm test

to see it pass!

  Index Page
GET / 200 10ms - 211b
    ✓ renders successfully 

  1 passing (34ms)

update: follow the rspec upgrade guide and try the transpec gem

Upgrading a Rails app to RSpec 3, I appreciated Jake Boxer’s guide. However, his regular expressions didn’t work in the editors I use, so I whipped up some command-line options that use the flexible unix utility sed.

If you want to actually understand the following scripts, I recommend Bruce Barnett’s sed tutorial and remember xargs is your friend.

Also, there are a few differences with sed on OSX, so these may not work on other platforms.

I didn’t attempt to handle multi-lined expressions, so I searched my spec directory for “lambda” in advance and changed those manually, and, of course, found a few other exceptions by running my tests afterwards.

Change “should ==” to “should eq”

You’ll need to change other operators also, but this was a pattern that happened to be very common in my code.

find . -type f -print0 | xargs -0 sed -i "" -E "s/^([[:space:]]+)(.*)\.should ==[:space]*(.*)[:space]*$/\1\2.should eq(\3)/g"

Change “whatever.should” to “expect(whatever).to”

This also works for should_not, since it is okay to say .to_not, even though I usually see people wrote .not_to

find . -type f -print0 | xargs -0 sed -i "" -E "s/^([[:space:]]+)(.*)\.should/\1expect(\2).to/g"

Also, check out: Cyrus Stoller’s upgrade tips

I just created a very lightweight time tracking system where we can use a Google Calendar to track who does what when and then get a spreadsheet that shows all of the hours worked by individuals.

It goes with a spreadsheet where the first row has a header like this:
Date Meeting Sarah Paul Glen

Screen Shot 2014-07-27 at 6.47.14 PM

The we just make events on Google calendar and invite whoever is working together at that time — could be a team meeting or one of us working on our own. Time will be tracked by first name of email address
( and would all get tracked in the “Paul” column). Currently the script doesn’t support two different people who share the same email name before ‘@’.

You just have to fill in your calendar id, which you can find if you choose Calendar Settings and scroll to the bottom of the first page (in Calendar Address section).

// add menu
function onOpen() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var menuEntries = [{name:"Calculate Hours", functionName: "calculateHours"}];
  ss.addMenu("Hours", menuEntries);
  // calcular al iniciar

function meetingSummary(cal_id){
  var hours = 0;
  var cal = CalendarApp.getCalendarById(cal_id);
  Logger.log('The calendar is named "%s".', cal.getName());

  var this_year = new Date(2013,0,1);
  var now = new Date()
  var events = cal.getEvents(this_year, now);
  var results = []
  for ( i = 0 ; i < events.length ; i++){
    var event = events[i];
    title = event.getTitle();
    var start = event.getStartTime() ;
    var end =  event.getEndTime();
    start = new Date(start);
    end = new Date(end);
    hours = ( end - start ) / ( 1000 * 60 * 60 );
    guests = event.getGuestList();
    data = {meeting:title};
    for (g in guests) {
      var email = guests[g].getEmail();
      var name = email.split('@')[0];
      Logger.log(email, name);
      data[name] = hours;
    data['date'] = start.toDateString();
  return results;

function calculateHours(){
  var cal_id = "put your cal id here";
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getSheets()[0];
  var headerValues = s.getRange("A1:F1").getValues();
  var columns = {}
  var num_columns = headerValues[0].length;
  for (i=0; i < num_columns; i++) {
    columns[headerValues[0][i].toLowerCase()] = i+1;
  //{'glen':5, 'paul':4, 'description':6, 'meeting':2, 'sarah':3, 'date':1}
  var title_column = 2;
  // from second row
  var results = meetingSummary(cal_id);

  for ( var row_number = 1; row_number < results.length+1 ; row_number ++){
    var result = results[row_number-1];
    for (name in columns) {
      Logger.log("..."+name+"   "+result[name]);
      if (typeof result[name] == 'undefined') result[name] = "";
      s.getRange(row_number+1, columns[name]).setValue(result[name]);