SOLID Code in NodeJS

Omar Faroque
4 min readNov 5, 2017

“Good design goes to heaven, the bad design goes everywhere”

The purpose of this article is to help the NodeJS community and learn from the community for writing better code in NodeJS. I was never been a full-time professional NodeJS developer until recent time although I am writing small script and services in NodeJS for a long time. From that last several year's experiences as a big of fan NodeJS, one thing is noticeable, most of the NodeJS developer writes code without clean architecture. I read hundreds of NodeJS projects code, blog. Unfortunately, this spaghetti code is everywhere. Blogger writes to help the community by creating a simple example, they don’t care much how you will make your codebase clean. So the problem arises, new developers read those blogs and start writing another spaghetti. As we know, 50% of the total developers in the world are new developers, spaghetti code will be there. Here I am particularly trying to figure out how can you make your router, validation, controllers and DB access separately.

The router is responsible for route requests to a particular controller and return a response to the client. The router should not validate and access DB or any external API. I noticed that the developer writes everything inside of their router/controller class what is a break of the SOLID principle. Here are the clean ways to do it:

Here is sample code example:

Here is snippet of the app.js file where “/users” request forwarded to the users.js route file.

const users = require('./routes/users');
app.use('/users', users);

Now lets got the route file below:

'use strict';

const express = require('express');
const router = express.Router();
const UserController=require('../controllers/user-controller');
const userValidation = require('../validations/users-validation');
const UserValidation = new userValidation();
router.post('/signup',UserValidation.validateInput, UserController.signup);
router.get('/signin', UserController.signin);
module.exports = router;

If you notice in the above code snippet, you will observe that I have put my validation separately and the router is calling the controller to communicate with a data source or any other external API. The router is only responsible for accepting the request and response the result back to the client. The controller will support the router. Let's see the controller now,

'use strict';

let userInteractor = require('../interactors/users-interactor');
const UserInteractor = new userInteractor();

exports.signup=function(req,res,next){
UserInteractor.saveUser(user).then(function (result) {
res.status('200').send(result);
}).catch(function (error) {
res.status('500').send(error);
});
};

exports.signin=function(req,res,next){
UserInteractor.getUser(user).then(result=>{
res.status('200').send(result);
}).catch(error=>{
res.status('500').send(error);
})
};

The controller is actually doing the business logic like where to call(in case of external API) or find the proper interactor. Here users-interactor.js is the interactor to communicate with DB or any other external services where you can put all of your logic.

Now look at the validation implementation:

'use strict';

class InputValidation {
constructor() {
// If you need to do any initializations here
}

/**
* This is a abstract method, all the validations subclass need to implement this method.
*
@param req Request parameters for incoming request.
*
@param res Response object to the client.
*
@param next pass the control to next task.
*
* */
validateInput(req, res, next) {
return new Error('Any subclass needs to implement this method');
}
}

module.exports = InputValidation;
'use strict';
const InputValidation = require('./input-validation');

class UserValidation extends InputValidation {

constructor() {
super();
}

validateInput(req, res, next) {
console.log(req.body.name);
console.log(req.body.email);
console.log('do your validation');
next();
}
}
module.exports = UserValidation;

Finally, look at the interactor file where I am accessing the database and my business logic.

Base Interactor:

'use strict';
const db = require('../models');

/**
* This is the parent class of all the interactors.
* Interactors is nothing but helper to communicate with any external api and db
* */
class Interactor {

/**
* Represents Interactor for communicating with external services or DB.
*
@constructor
* */
constructor() {
this.init();
}

/**
* Initialize all needed abjects to communicate with db or any external api.
* */
init() {
this.db = db;
}

/**
*
@return Reference of the db object to communicate with database
* */
getDB() {
return this.db;
}
}

module.exports = Interactor;

User Interactor(One implementation of the base interactor):

'use strict';
const interactor = require('../interactors/interactor');

class UserInteractor extends interactor {
constructor() {
super();
}

saveUser(user) {
return new Promise(resolve => {
/**
*Get the reference of User model
*/
const User = this.getDB().User;
/**
* Build user object to save into db
* */
let userModel = User.build(user);
/**
* Save use object
* */
userModel.save().then(function (savedUser) {
resolve(savedUser);

}).catch(function (err) {
resolve(err);
})

}, reject => {
reject('something went wrong');
});
}

getUser(user) {
return new Promise(resolve => {
resolve("User found");
},
reject => {
reject('User not found');
});
}
}

module.exports = UserInteractor;

Sample code can be found here:

NOTE: Experts, please add your valuable comment to make better.

Follow me on LinkedIN

--

--