Menu

Building an Express.js application with Session Handling

By Aravindan Chandrasekaran | Open Web

Mar 03

In this blog post, we look at how to integrate session handling capability within an Express.js application along with Cassandra database. 

We have come a long way since we started learning Node.js.  But any practical web application is incomplete without session management. And behind the scenes. there is a database that does the heavy lifting of storing and retrieving the user/session data. So now that we have covered quite a bit of Node/Express.js, its time to think of session. And to achieve that, the first step is database integration. We decided to try out Apache Cassandra as it is a very robust, distributed database and is well suited for today’s big data applications. So let’s dive in and build a session aware Node/Express application.

Introduction to Apache Cassandra

Apache Cassandra is a highly scalable, distributed and high-performance No-SQL database. Cassandra is designed to handle a huge amount of data with its distributed architecture. Cassandra supports clustering and replication which enables high availability and reliability.

Features of Cassandra

  1. Massively Scalable Architecture: Cassandra has a master-less design where all nodes are at the same level which provides operational simplicity and easy scale out.
  2. Masterless Architecture: Because of the master-less design, data can be written and read on any node.
  3. Linear Scale Performance: As more nodes are added, the performance of Cassandra increases.
  4. No Single point of failure: Cassandra replicates data on different nodes that ensures no single point of failure.
  5. Fault Detection and Recovery: Failed nodes can easily be restored and recovered.
  6. Flexible and Dynamic Data Model: Supports datatypes with fast writes and reads.
  7. Data Protection: Data is protected with commit log design and built-in security like backup and restore mechanisms.
  8. Tunable Data Consistency: Support for strong data consistency across distributed architecture.
  9. Multi Data Center Replication: Cassandra provides feature to replicate data across multiple data center.
  10. Data Compression: Cassandra can compress up to 80% data without any overhead.
  11. Cassandra Query language: Cassandra provides query language that is similar like SQL language. It makes the migration very easy for relational database developers who want to move from relational databases to Cassandra.

Cassandra Installation and Setup

Follow this installation guide by Digitalocean. With this, you will be able to set up a Cassandra single node cluster. Remember to use your local Linux/Ubuntu system instead of the Digital Ocean droplet.

Once Cassandra database is set up, we can start creating keyspaces and tables using the Cassandra cqlsh  tool.

Using cqlsh, we can

  1. define a schema,
  2. insert data,
  3. execute a query.

Start cqlsh using the command cqlsh as shown below.

$ cqlsh 127.0.0.1

If you are familiar with SQL, then the commands under cqlsh will be self-explanatory for you. Some of the common cqlsh commands that are used for standard database operations are,

  1. DESCRIBE – Describes the current cluster of Cassandra and its objects.
  2. EXIT –  Terminate cqlsh session.
  3. CREATE KEYSPACE – Creates a KeySpace in Cassandra.
  4. USE – Connects to a existing KeySpace.
  5. ALTER KEYSPACE – Change the properties of a KeySpace.
  6. CREATE TABLE – Create a table in a KeySpace.
  7. INSERT – Add columns for a row in a table.
  8. UPDATE – Update a column of a row.
  9. DELETE – Delete data from a table.
  10. SELECT – Read and fetch data from a table

 

Let’s create one KeySpace and table in cql shell,

CREATE KEYSPACE userdetails WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};  

This command creates a KeySpace named ‘userdetails’ with basic configurations like class and replication factor.

Now using the USE command, connect to the ‘userdetails’ KeySpace,

USE userdetails;

Create a table with column values username, email and password with username as the primary key,

CREATE TABLE users(username text, email text, password text,PRIMARY KEY(username));

With this, we have set up Cassandra database. We will be using this for enhancing the login demo application that we created in the last post. Next, we want to setup the session handling in Express.js.

Session Management

Using sessions to keep track of users, as they journey through our application is an important feature in many web portals.  As usual, using Express with Node.js makes it super simple to get sessions up and running.

But before we dive in, there is a little bit of setup necessary. We need to “use” the cookie parser middleware. and the session features of Express before the sessions functionality can be made available. And we need to do this before defining any routes, which in our case would look like this

app.use(cookieParser());    
app.use(session({
secret: 'example',
cookie:{
maxAge:60000
},
resave: false,
saveUninitialized: true
}));

Since sessions use cookies to keep track of users, we need both the cookie parser and the session framework. It is important to note that the cookie parser is used before the session and this ordering is required for sessions to work. We have also provided a secret to the session initializer, which provides a little more security for our session data. Of course, you might want to use a key that is a more secure but for now this will suffice.

Sessions are accessible through the request object in each route. Let’s explore the code in our enhanced login demo application to see how can we use Cassandra and session management in Node/Express.

Demo Express Application with Cassandra and Session Management

In this enhanced login demo that we are going to build today, we’ll have session based login system where all user login credentials are stored in Cassandra database. With the session, only logged in users can access the restricted pages. The session is also configured for auto logout with session cookie storage for a pre-defined time interval.

The source code for this demo is available in this GitHub repo.

Let’s see the steps in creating this application. We are ignoring the Node app creation from the scratch and will directly jump to the application functionality that is dependent on database and session.

Step 1: Login page

First, build the login page which gets the user credentials and also has the provision to register new users.

Step 2: User access pages

Using jade templating engine, create some user pages which will be placed in the view directory of our application. We will also need configuration for using jade and setting the view directory in our application main file, app.js.

app.set("view engine","jade")
app.set('views', path.join(__dirname, 'views'));

Step 3: Cassandra initialization

In routes.js file, all the database functions are defined. But before that, Cassandra driver has to be imported. To install Cassandra driver, do the following

 npm install cassandra-driver --save

And then we can use ‘require’ to import Cassandra to our application and initialize it

var cassandra = require("cassandra-driver");
var connection;
var db_config = {
contactPoints : ['127.0.0.1'],
keyspace:'userdetails'
};
connection = new cassandra.Client(db_config);
connection.connect(function(err,result){
console.log('cassandra connected');
});

Here the connection to our cassandra cluster is done using this new cassandra.Client(db_config).connect; command.

Step 4: Cassandra Query

Two major operations we are going to do in this application are,

  1. Insert (During Register)
  2. Select(During Login)

Insert

Insert operation is used for registering new users with the application. Once the user registers, we’ll redirect them to the user home page and start the session for the user.

app.post("/register", function (request, response) {
var body = request.body;
var usersessId = body.regUsername;
var hash = crypto.createHash('md5').update(body.regPassword).digest("hex");
var query = 'INSERT INTO users ( username, email, password) VALUES (?, ?, ?)';
var params = [body.regUsername, body.regEmail, hash];
connection.execute(query, params, { prepare: true }, function (err) {
if(err) {  
throw err;
} 
});
console.log("user registered");
request.session.sessId = usersessId;
response.render('home', {title: "HOME", username: body.regUsername +" is registered & authorized"});
});

Select

Select operation is done to query the database for a user and match their password to authenticate the user during login.

app.post("/login", function (request, response) {
var body = request.body;
var usersessId = body.Username;
var hash = crypto.createHash('md5').update(body.password).digest("hex");
var postVars = {username: body.Username, password: hash};
// fetching from CASSANDRA
var queryString = "SELECT password FROM users where username = ?;";
connection.execute(queryString,[body.Username], function(err, rows) {
console.log("rows",rows);
if(err){ 
handleDisconnect();                                  
throw err;
} 
if(rows["rows"].length == 0){
// console.log(body.Username)
response.render('unauthorized', { title: "unauthorized", username: body.Username +"U r unauthorized man ..!!"});
}
else{
var passwordSuccess = 0;
for (var i in rows["rows"]) {
if(rows["rows"][i]["password"] == hash){
console.log("USER SUCCESS");
passwordSuccess = 1;
break;
}
else{
passwordSuccess = 0;
}
}
if(passwordSuccess == 1){
request.session.sessId = usersessId;
response.render('home', { appTitle: "HOME", username: body.Username +" home page .. welcome !!"});
}
else{
response.render('unauthorized', {title: "unauthorized", username: body.Username +" wrong password"});
}
}
});
});

Step 5: Express Session

In the above steps, we have achieved the register (sign up) and signin(login) operations. For the login, we also need to initiate a session. In Express, this can be one using two varients

  1. cookie based session
  2. session store

We have used  cookie based session management in our application. As you can see in the code, we have stored username as the session-id .

 request.session.sessId = usersessId;

So when the user logs out of the application, the user’s session id will be destroyed and the user is redirected to root of the application.

app.get('/logout', function (request, response, next) {
request.session.destroy(function(err){
if(err){
console.log(err);
}else{
response.redirect('/');
}
});
});

Summary

You can explore the GitHub code base to understand more about how to enable and disable session based access to other pages of the demo app. This will help you to think in terms of building a practical web portal application which applies many restrictions to non-logged in users.

With this, we have achieved an important milestone. Please feel free to leave your queries in the comment section and I will respond to you at the earliest. In the next post of this series, we will cover yet another important aspect of building portal applications, the REST API. See you soon.

 

Follow

About the Author

Aravind is a technology enthusiast. He is an expert in conceptualizing and developing niche apps around IoT and cloud computing and is aspiring to be a full stack developer.