Sessions with Node.js and Express

Node.js, programming

I cannot believe how difficult it was to do something so simple. The bulk of the reason behind this, I believe, is the lack of documentation.

It always starts at App.js:

var express = require('express');
...
var app = express();
app.configure(function() {
...
app.use(express.bodyParser());
...
app.use(express.cookieParser('secret'));
...
app.use(express.session());
app.use(app.router);
...
});

...
app.get('/', routes.index);
app.get('/loginf', routes.loginf);
app.post('/login', routes.login);
app.get('/logout', routes.logout);

These set up the app to:

  • bodyParser: parse form submissions and put the parsed parameters into req.body
  • cookieParser: parse cookie values when present
  • session: session support
  • router: URL routing support
exports.index = function(req, res) {
  // If the session has a username, echo it to the view
  res.render('index', {username:req.session.username});
};

exports.loginf = function(req, res) {
  // render the login form
  res.render('loginf', {});
};

exports.login = function(req, res) {
  // extract the submitted form and record the user name
  // in the session
  var userName = req.body.username;
  var password = req.body.password;
  req.session.username = userName;
  res.redirect('/');
};

exports.logout = function(req, res) {
  // clear out the session
  req.session.destroy();
  res.redirect('/');
};

The index view (views/index.jade) either displays the current user name or offers a link to log in.

doctype 5
html
 body
  - if (typeof username != 'undefined') 
   div User #{username} 
   div 
    a(href='/logout') Log out
  - else
   div
    a(href='/loginf') Login please
html
 body
  table
   form(method='POST',action='/login')
    tr
     td
      div User Name
     td
      input(type='text',name='username')
    tr
     td
      div Password
     td
      input(type='text',name='password')
    tr
     td(colspan='2')
      input(type='submit',value='Log in')

Turns out the indentation matters in Jade, just like in Python. If A and B are in the same column, they are siblings. If B is indented further than and below A, then B is a child element of A.

Things yet to be figured out

All calls to render the ‘index’ view (or any other view that needs to know whether a user is logged in) currently needs to explicitly set the model’s “username” property. Is there an easier way? That is, can a Jade file access the session object?

Is there an interception/filter mechanism to reroute to the ‘loginf’ view if there is no “username” in the session, so that I don’t have to do this on every controller that requires a logged in user?

Turns out the answer is the various “middleware” components appended to “app” in app.js. This article describes it.

Debugging Node.js on Windows

Node.js, programming

Confluence

One annoying thing about Open Source and/or free stuff in general is that things are sparsely documented, if even. Take the n packages and, due to the ways they interact, I may have n! possible steps to integrate.

Here are the components of my environment:

  • Windows (Operating system)
  • Eclipse (IDE)
  • Chrome (Web browser)
  • Node.js (language)
  • Express (Web framework)
  • node-inspector (debugger add-in)

Fundamental prerequisites

All these “big” install are pretty much straight-forward. Note that node-inspector seems to work well only with Chrome; IE and Firefox don’t seem to work for me, possibly due to the Webkit implementations (or lack thereof).

Web app set-up

Install Express:

npm install -g express

Install node-inspector:

npm install -g node-inspector

Create web-app (short way w/ Express):

express myapp
cd myapp
npm install

Start Debugging

Installing nodeclipse plug-in

Follow the direction from the nodeclipse page to install via Eclipse Help | Install New Software …

My node.exe is installed as:

C:Program Files (x86)nodejsnode.exe

But the nodeclipse plug-in assumed that it is c:program filesnodejsnode.exe, so I had to change the location to the node.exe file via Windows | Preferences | Nodeclipse.

Then I opened the Node perspective via Windows | Open Perspective | Other … and picked Node.

From there, create a new Node project and import from myapp. Eclipse should know how to structure the resources now that the nodeclipse plugin is installed.

Load up app.js and click the debug button from the toolbar.

Start node-inspector:

Open up a command prompt

C:Program Files>node-inspector
 info - socket.io started
visit http://0.0.0.0:8080/debug?port=5858 to start debugging

Despite the message, Chrome needs to hit this instead: http://localhost:8080/debug?port=5858 (http://127.0.0.1:8080/debug?port=5858 also works; just not 0.0.0.0)

Clicking the “Scripts” toolbar button should reveal a list of JS files to open in which breakpoints can be placed.

Alternative: Debugging without Eclipse

The Eclipse IDE helps with code editing, but since node-inspector is used for debugging, Eclipse is not strictly required for debugging. To start debugging on a command prompt without Eclipse:

cd myapp
node --debug app.js

Alternative: Debugging without node-inspector

See https://github.com/joyent/node/wiki/Using-Eclipse-as-Node-Applications-Debugger

This will install the “ChromeDevTools” and “Chromium JavaScript *” plug-ins into Eclipse. Note that these should not replace the “Nodeclipse” plug-in. The former are debugging tools, and the latter is for editing and running Node.js files.

The only cumbersome part I found is that, to debug a Node.js app, there are two steps instead of one:

  • Start to debug the Node.js app itself from the Node perspective, using the “Node Application” configuration added by the Nodeclipse plug-in. (I think it also works if I use the “Chromium JavaScript” configuration.) This will set the app up to listen to localhost:5858 for debugging.
  • Now create a new debugging configuration using the “Standalone V8 VM” configuration and set the port to 5858. Start that configuration also so that it will attach to the first.

For someone from a Java background, I guess this is similar to running a Java app with “-Xdebug -Xrunjdwp:server=y, transport=dt_socket,address=5858” and then attaching a debugger to port 5858.