Node.js and Express.js sessions using MarkLogic 8

by Matt Pileggi

Overview

Most web applications benefit from the use of sessions. Apps built with Node.js are no different, and the Express server makes it very easy to apply middleware to help you manage your user's sessions. In this quick start guide we will be using connect-marklogic, which is an implementation of connect-session. Connect-session provides the plumbing for creating and managing sessions between browser coookies and the db, and connect-marklogic allows us to use MarkLogic 8 as the persistence layer. It's very simple!

Requirements

Before you get started with the steps below, you'll need to be familiar with and have already installed the following:

Express

In its own words, Express is a fast, unopinionated, minimalist web framework for Node.js. It is a very popular choice when building Node applications. I will take the minimalist approach here, as well, and show you just how easy it is to get started.

The first thing we will need to do is install express itself.

npm install express

Then we can create our server.js file.

// very basic express server with MarkLogic 8
var express = require('express');
var app = express();

app.get('/', function(request, response) {
  response.send('Hello');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://%s:%s', host, port);
});

That's it for now. Save this file and run 'node server.js' ('npm start' will do the same thing if you have a package.json file). You'll notice the output telling you the server is running at 3000. If you visit http://localhost:3000 you should be greeted with a "Hello". This is the app.get('/') path that we've set up in our server. Unfortunately, it is the only path that is supported by our server and it will always serve the static message. Not exactly a capable application, so let's add some session magic!

Express-Session

We'll need to add another dependency in order to work with sessions.

npm install --save connect-session

Connect-session is the glue between Express, the browser, and the session store for managing a client's session. This glue is called "middleware" and it is a common pattern when dealing with Express. In order to use the connect-session middleware we must configure it and inform our Express server of how to use it. Add the following to your server.js file before the app.get('/') line from earlier.

var session = require('express-session');

app.use(session({ secret: 'enterprise nosql'}));

What we've done now is import the express-session module and then created a new default instance of it. App.use without a path tells Express to use the session middleware for ALL requests, regardless of path or method. This way the session data will be evaluated and updated on each request. The middleware also exposes the session directly on the request object so it's easy to use! Update server.js and replace the previous route with the following:

app.get('/', function (req, res) {  

  if(req.session.username) { 
    res.send('<p>Hello, ' + req.session.username+ '!</p><p><a href="/logout">logout</a></p>');  
  } else {
    res.send('<form action="/login"><label for="username">Username</label> <input type="text" name="username" id="username"> <p><input type="submit" value="Submit"></p></form>');  
  }

});

This will inspect the req.session object from the middleware for a property called 'username'. If username is in the session then we will greet the user by name, otherwise we will display a form for them to login. In order for that to work properly, we need to add two more routes. Add the following below the previous route in server.js:

app.get('/login', function(req, res) { 
  if (req.query.username) {  
     req.session.username = req.query.username;
  } 
  res.send('<p>You have been logged in, ' + req.session.username+'</p><p><a href="/">Home</a></p>');
});
app.get('/logout', function(req,res) {
  delete req.session.username;
  res.send('<p>Sorry to see you go!</p><p><a href="/">Home</a></p>');
});

We now have three paths: /, /login, and /logout. /login expects a query param named 'username', which will be set onto the session. Start up server.js and give it a shot. You should have a series of login and logout steps that are able to remember who you are!

MarkLogic

The application we've built so far makes easy work of managing a user's session. However, the default SessionStore implementation of the middleware is in-memory storage. This means that all of your session data will disappear when the server restarts. Our app is not exactly production-ready just yet! In order to fix this, we need to configure the express-session middleware so that it uses a more permanent session store. Enter MarkLogic.

npm install marklogic connect-marklogic

We will need the marklogic module to create our connection to a MarkLogic database, and the connect-marklogic module provides the session store implementation that express-session requires. Sound complicated? Don't worry, it's quite easy. Add the following lines to your server.js file right below the declaration of var session:

var marklogic = require('marklogic');
var MarkLogicStore = require('connect-marklogic')(session);
var mlClient = marklogic.createDatabaseClient({  host: 'localhost',  port: '8000',  user: 'admin',  password: 'admin',});

This imports both of our new modules. We initialize connect-marklogic with the session middleware, and create a new database client that we will use to persist the sessions. The only step left is to modify the configuration of our session middleware so that it can use our session store. Modify the app.use line so that it looks like this:

app.use(session({
  store: new MarkLogicStore({ client: mlClient }),
  secret: 'enterprise nosql'
}));

The only thing we needed to add was the 'store' option when instantiating our middleware. Express-session will now read and write all session data to our MarkLogic database! Start up your server.js and you can visit the same endpoints from earlier. However, you can now stop and restart the server and the session data will remain!

All Together Now

Here's the final version of server.js in case you want to compare notes.

// very basic express server with MarkLogic 8
var express = require('express');
var app = express();
var session = require('express-session');
var marklogic = require('marklogic');
var MarkLogicStore = require('connect-marklogic')(session);
var mlClient = marklogic.createDatabaseClient({  host: 'localhost',  port: '8000',  user: 'admin',  password: 'admin',});

app.use(session({  store: new MarkLogicStore({ client: mlClient }),    secret: 'enterprise nosql'}));

app.get('/', function (req, res) {  
  if(req.session.username) {
     res.send('<p>Hello, ' + req.session.username+ '!</p><p><a href="/logout">logout</a></p>');
    } else {
        res.send('<form action="/login"><label for="username">Username</label> <input type="text" name="username" id="username"> <p><input type="submit" value="Submit"></p></form>');
    }
});

app.get('/login', function(req, res) {
   if (req.query.username) {
       req.session.username = req.query.username;
  }
  res.send('<p>You have been logged in, ' + req.session.username+'</p><p><a href="/">Home</a></p>');
});

app.get('/logout', function(req,res) {
  delete req.session.username;
  res.send('<p>Sorry to see you go!</p><p><a href="/">Home</a></p>');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://%s:%s', host, port);
});

More Options

There are more options available when creating your connect-marklogic instance. For more details visit the repository https://github.com/withjam/connect-marklogic.

Be sure to keep up with MarkLogic 8 and MarkLogic's other tools for Node.js development!

Comments

  • The npm bits are a little easier to handle in a package.json file. I found that I had to install connect-marklogic from git because npm couldn't find it. Gist: https://gist.github.com/mblakele/d5b87cb83b4a04faed09 If you test the server without the MarkLogicStore, and then add the MarkLogicStore, your browser will have a cookie for a session that doesn't exist. This throws RESTAPI-NODOCUMENT on the server, logged to ErrorLog.txt, and shows a cryptic 404 on the web page: "Error: read documents: cannot process response with 404 status (on /sessions/....json)". Try removing the cookie named connect.sid if this happens to you.
    • Another update on this. I didn't realize that not found documents invoked the error callback in the Node API. The latest version (0.1.1) will now silently fail if ML returns 404 on the session lookup - which will cause Express to generate a new session id.
    • Thanks for the feedback. I hadn't actually published to NPM as I was waiting for ML8 to release officially. I have since published to npm so you should be able to install as instructed. I've also updated the version now that I've tested with the current ML8 release. There was a bug in 0.0.9 where it was reading the document descriptors rather than the document contents. That has been fixed in 0.1.0. Thanks, also, for the heads up about session id collision when following the guide!