The Importance of Documentation or “How i discovered how to share Express sessions with Socket.io”.

Hi guys, it’s been a little while since my last post, i’ve been working on a small project — building a generic social network app with the VMENS (Vue.js, MongoDB, Express, Node, Socket.io) stack. This post begins a series of posts in which i share my experiences building the app and how i resolved several issues i encountered. As usual, the code for this app is available on github.

Background:

Socket.io is a really great library for adding real time functionality to our applications. As Javascript Web developers, socket.io is one of the major tools we use in addition to the NEM (Node, Express and MongoDB) stack for building DIRTy (Data Intensive Real Time) web applications.

However, a serious issue occurs when we have an idea for a feature or functionality and we’re studying the documentation but to our frustration — we don’t find detailed info with respect to the issue we’re trying to resolve.

I found myself in this situation quite recently while building a project, i wanted a way to share an Express session with Socket.io without needing to include the express-socket.io-session library or change my code architecture style by conforming to express.io styles or using redis etc, what i wanted and felt was possible from my initial research was a ‘native’ way to share an Express session with socket.io in order to allow any route handler access to the underlying socket instance of the connected client, and also to provide the socket instance of the connected client access to the serialized user from the session store (and access to all that user’s data as necessary).

I didn’t want to use the work around of storing socket ‘nicknames’ in an object and then accessing those sockets by nicknames — this would merely provide the route handlers with access to the underlying socket but not give the socket access to the serialized user.

After much searching, i found a path that led to the solution i eventually adopted.

The solution is surprisingly simple. It’s just not very well documented. It is possible to use the express session middleware as a Socket.IO middleware too with a small adapter like this:

The Problem:

From the quote above, we can see what “the problem” is — a solution to an issue which wasn’t well documented. Furthermore (at least for me..) i wasn’t quite clear on how exactly i would access the underlying serialized user (from the socket.io module) or socket (from my route handlers). So i had to do some digging and tinkering before developing a solution which i’m very pleased with.

Join me as we walk through the process of understanding together.

Elements of a solution:

From the resource which i shared earlier on, we notice that the ‘key’ to achieving our goal is to share the session middleware we use within our Express app with the socket.io instance. Specifically this consists of 2 steps (taken directly from the post, but we will soon use our own versions):

  1. Within ‘app.js’ i.e. the main module for the app (i.e. as used in the post) we do the following:
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: “keyboard cat”,
});
app.use(sessionMiddleware);

2. And also in that same module, presuming that we’ve ‘required’ the socket.io package we do:

sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});

The preceding steps then make it possible for the following:

sio.sockets.on("connection", function(socket) {
socket.request.session // Now it's available from Socket.IO sockets too! Win!
});

The preceding 2 steps are the elements for the solution we will build, however, as is, there’re some questions that need to be answered.

  1. What exactly does “socket.request.session” expose?
  2. How is the exposed object from (1) above related to the ‘req.session’ object? NB we are assuming that the session middleware module we are using is ‘express-session’.

Lets’s answer these 2 questions in order to gain a deeper understanding of what the submitted solution makes possible and we will then be able to understand just what we need to do in order to modify the solution for our own needs.

What’s exposed by “socket.request.session”:

To answer this question, we’ll build a small app, furthermore, rather than placing everything within one file we will split the app into modules just as we would for a ‘non trivial’ app.

The app will have the following views:

login.html

The login screen which serves as the homepage.

signup.html

The signup page for new users to sign up for access to the app.

dashboard.html

Upon successful signup, a dashboard is rendered. Notice a couple of things:

  1. In the exposed console, you see that some data is received from the socket connection, specifically a firstName and lastName.
  2. On the dashboard, this data is displayed as the user’s full name.
dashboard_disconnected

When the client is disconnected from the server, we notice that the user’s full name is no longer displayed.

dashboard_reconnected

Upon successful reconnection, the data is received from the socket and the full name is displayed once again.

For your convenience you can download or copy the following project scaffold script:

This will help us quickly scaffold an application. I’m going to jump ahead a little here but i promise we’ll tie everything together neatly. We will need to implement local authentication using passport, and this means we’ll also need to build at least one ‘user’ model via mongoose. So we first run ‘npm init’ to create a ‘package.json’ file then download or copy the following script to run ‘npm install’ with the modules we will use.

Having installed our modules we quickly build out the following app modules — app.js, socket.js, users.js, routes.js, config.js, development.js, passport.js, and isLoggedIn.js. We also build 3 views login.html, signup.html, dashboard.html.

The 3 modules we are interested in are ‘app.js’, ‘routes.js’ and ‘socket.js’.

app.js

For us, the interesting portions of this file are from lines 90–100, we see how the session middleware is abstracted into a reusable variable (lines 90 — 92), then we are able to 1) use it in the Express app as usual (line 97) and 2) just as suggested by the resource where we discovered the elements we need we are able to use it as middleware for socket.io (lines 98 — 100).

NB We had previously ‘required’ socket.io on line 14.

The work done on these lines lay the foundation for what follows.

socket.js

The interesting portions for us are lines 22–28 and 54.

On line 22, we finally answer the question — what is exposed by “socket.request.session”. If we examine our terminal we should see something similar to the following…

the socket session object Session {
cookie:
{ path: ‘/’,
_expires: Fri Mar 04 2016 16:33:41 GMT+0100 (WAT),
originalMaxAge: 86400000,
httpOnly: true },
passport: { user: ‘56d859553443f3d409df1de2’ },
lastModified: Thu Mar 03 2016 16:33:41 GMT+0100 (WAT) }

NB (the assumption is that a user has logged on to the app).

Notice the ‘passport’ property? Line 23 allows us to see just what this data is, in the terminal you should have something like…

the actual serialized user from passport 56d859553443f3d409df1de2

If you check your MongoDB database, you’ll be pleasantly surprised to see that this value is the value for the serialized user of the session.

On line 28, we map the underlying socket instance to the serialized user. This allows us to access the underlying socket from anywhere in our app by simply passing a reference to the user’s “_id” to the ‘userSockets” object exposed by line 54.

Lines 29–39 are responsible for using the “_id” of the serialized user to access the database and retrieve additional data about the user which isn’t exposed by the actual endpoint (we’ll see this in routes.js).

Having answered question 1, we can answer question 2.

How is the exposed object from “socket.request.session” related to the ‘req.session’ object?

To answer this question we need to examine ‘routes.js’.

For our purposes, the interesting portions are lines 84–95.

On line 92, we examine the actual ‘req.session’ object which the ‘express-session’ middleware extends the base ‘req’ object with when session support is enabled for an Express app.

In the terminal we should have something like this…

the request session object Session {
cookie:
{ path: ‘/’,
_expires: Fri Mar 04 2016 16:33:41 GMT+0100 (WAT),
originalMaxAge: 86400000,
httpOnly: true },
passport: { user: ‘56d859553443f3d409df1de2’ },
lastModified: Thu Mar 03 2016 16:33:41 GMT+0100 (WAT) }

If we compare this object with that exposed by “socket.request.session” we’ll notice they’re the exact same object! How cooool is that? And because of our access to the ‘userSockets’ object, we can do some interesting stuff (i’ll show an example real soon).

The full solution:

While the initial pointer from the resource was helpful, we find that we gain extra cool benefits by actually working with the ‘_id’ of the serialized user i.e. what we really need access to is “socket.request.session.passport.user” rather than simply “socket.request.session”. The reason as we know by now is that the former allows us to give the socket server access to the underlying user object from the datastore and this allows us to do very cool real time stuff useful for applications like social media apps (details in a future post).

By the way, you may want to examine ‘dashboard.html’ which shows how we insert and remove the additional data received from the socket into the DOM.

Conclusion:

As promised, let’s briefly review how we can use our new understanding to do some cool stuff. Below is a snippet of the socket.io code used in the social network app i just finished working on.

When a user makes a comment, socket.io is able to notify only the user’s friends by accessing the “_id” of the serialized user (i.e. the ‘ID’) variable as seen on line 3.

For us, the key to this feature is line 3! By having access to the “_id” value for the user making a comment (as we have come to understand, this is possible by sharing the Express session with socket.io), we can use async.each() (called ‘controlFlow’ in this example) to iterate over each friend in the user’s “friend’s” array and then by accessing their own “_id”, we can emit the user’s action to just that person — how cool is that?

Thanks for reading, as usual please leave comments below or contact me for any clarification.

NB I’ll be starting a YouTube channel soon… keep following!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.