Simplifying Your Code with Express Middleware

The first time I created a user login page with authentication I just kept chaining my functions inside of each other.

I had code that looked like this

//server.js
app.post('/signup', userController.createUser);

When a post request is sent to the server all that I know on first look is that a user is created.

// userController.js
userController.createUser = (req, res) => {
const user = new User(req.body);
User.create(user, (err) => {
if (err) {
res.render('./../client/signup', { error: err });
} else {
cookieController.setSSIDCookie(req, res, user.id);
res.redirect('/profile');
}
});
};

So I go over to my userController file and look at the function. I see that a new user is created. If the creation succeeds, a cookie is set in the cookie controller.

// cookieController.js
function setSSIDCookie(req, res, id) {
res.cookie('ssid', id, { httpOnly: true });
sessionController.startSession(id);
}

I then have to go over to my cookieController file to see what happens next. I see that a cookie is set, then another function is called that calls a function in the sessionController.

// sessionController.js
sessionController.startSession = (cookieId) => {
const id = JSON.stringify(cookieId);
const newSession = new Session({ cookieId: id });
Session.create(newSession, (err) => {
if (err) {
console.log(err);
}
});
};

The session controller then finally creates a new session from a mongoose schema. In order to find out how exactly a user is being created I had to search through three other files. Navigating through functions without express is tedious and it is tough to figure out what is going on.

An easy way to solve this problem is to modularize your code with express middleware.

// server.js
app.post('/signup',
userController.createUser,
cookieController.setSSIDCookie,
sessionController.startSession
);

This is an example of using express middleware. In the server.js file it is very easy to see the progression of how a user is created.

// userController.js
createUser: (req, res, next) => {
if (req.body.userName && req.body.password) {
const user = new User(req.body);
User.create(user, (err) => {
if (err) console.log(err);
else next();
});
}
}

It is easy to see that a user is created based on a mongo schema. Then we go and see that the next thing that happens in the post call is setting a SSID Cookie in the cookieController.

// cookieController.js
setSSIDCookie: (req, res, next) => {
User.findOne({ userName: req.body.userName }, (err, found) => {
if (err) throw err;
if (found) {
var userId = found['_id'];
res.cookie('ssid', userId);
res.ssid = userId;
next();
}
});
}

If a user is found a cookie is added to the response. I am also adding a key value pair to the response in order to use that SSID in my sessionController.

// sessionController.js
startSession: (req, res) => {
const newSession = new Session({ cookieId: res.ssid });
Session.create(newSession, (err) => {
if (err) throw err;
res.sendStatus(200);
});
}

A session is created and a status of 200 is sent to signal that the post was successful.

With express middleware, it is still neccesary to walk through all of the middleware to know exactly what is happening, but before you even start walking through the code you know generally what is going to happen.

Their is also another great perk to using express middleware. If a post request is written in the same way as the first, the only way to reuse it in a separate project is to copy out all of the requisite code.

If you write your post requests using middleware it is very easy to extract single functions in order to be used in other projects.