Shams Nahid
Shams Nahid's Blog

Shams Nahid's Blog

Building a scalable boilerplate with Node.js and Express

Building a scalable boilerplate with Node.js and Express

Shams Nahid's photo
Shams Nahid

Published on May 17, 2017

6 min read

Check the source code here.

In computer programming, *boilerplate code or [boilerplate](en.wikipedia.org/wiki/Boilerplate_code)** refers to sections of code that has to be included in many places with little or no alteration. It is often used when referring to languages that are considered verbose*, i.e. the programmer must write a lot of code to do minimal jobs.

What ????

There is a saying, “Express is the best without it’s generator”. Express generator provides far more than your requirements(like some template engine and etc. etc.). To avoid that fat girl, just get what is required and here I am handling that situation. This boilerplate avoids all the front-end stuff and only focus over the server side.

Target Audience

Hope you know the basic of Node.js and Express.js.

Architecture

*├── app/
│   └── controllers/
│       └── dummyController/
│           └──index.js
│       └── test/
│           └── runner.js
│   └── models/
│       └── dummyModel/
│           └── index.js
│   └── routes/
│       └── endPoints
│           └── index.js
├── config
│   └── index.js
├── Test
│   └── utils.js
├── package.json
├── server.js*

Let’s see whats going there.

Start with the package.json file…

{
  **"name"**: **"boilerplate"**,
  **"main"**: **"server.js"**,
  **"scripts"**: {
    **"start"**: **"node server.js"**,
    **"test"**: **"mocha --recursive **/test/runner.js"
  **},
  **"dependencies"**: {
    **"body-parser"**: **"^1.17.1"**,
    **"express"**: **"^4.15.2"**,
    **"express-session"**: **"^1.15.2"**,
    **"mongoose"**: **"^4.9.8"**,
    **"morgan"**: **"^1.8.1"
  **},
  **"devDependencies"**: {
    **"babel-eslint"**: **"^7.2.3"**,
    **"cors"**: **"^2.8.3"**,
    **"mocha"**: **"^3.3.0"**,
    **"mocha-logger"**: **"^1.0.5"**,
    **"nodemon"**: **"^1.11.0"**,
    **"should"**: **"^11.2.1"
  **}
}

Some parameter like name, main are described *here*.

name

I set the name parameter to “boilerplate”. You should be sincere about the naming, it does not take space, dot or something.

main

The main field is a module ID that is the primary entry point to your program. That is, if your package is named foo, and a user installs it, and then does require(“foo”), then your main module’s exports object will be returned. This should be a module ID relative to the root of your package folder

Scripts

Scripts is for automate our development and the testing.

“start” script is using the default value of npm package. Since our server is named as server.js so it won’t affect if you remove the start “script”. Otherwise you had to change the “start” script according to the server name to use “npm start” to run the application.

“test” script run the ‘runner.js’ recursively if the file is under the directory ‘test’. Check these two links *1 [2](gist.github.com/timoxley/1721593)*

Dependency

dependencies are required for the application while devDependencies are only required on development phase. After deploying the application, the devDependencies will be ignored.

Server

Time to dig around the server.js

**var **express = require(**'express'**);
**var **app = express();
**var **bodyParser = require(**'body-parser'**);
**var **logger = require(**'morgan'**);
**var **mongoose = require(**'mongoose'**);
**var **session = require(**'express-session'**);

**var config **= require(**'./config'**);
**var **router = require(**'./app/routes/endPoints'**);

**var **port = process.**env**.PORT || 3000;

mongoose.connect(**config**.database.**db**, (err) => {
    **if**(err) {
        **console**.log(**'Error in connecting database : ' **+ **config**.database.**db **+ **" Error: " **+ err);
        res.send(500, {**err**: err});
    }
});

app.set(**'afterMathShady'**, **config**.database.**mySecret**);

app.use(bodyParser.**urlencoded**({**extended**: **false**}));
app.use(bodyParser.**json**());

app.use(logger(**'dev'**));

app.use(**'/api'**, router);

app.listen(port);

**console**.log(**'Server running on port: ' **+ port);

I grab the middle-ware module and set them. It’s important to keep the sequence. For example, if you use the router before the body-parser the application will behave weirdly. It won’t get the address parameter and request body properly.

If the application is not in production, then the local port number will be 3000. Also any error on connecting the database will be displayed in console. Another good practice is to set the logger in development mode.

CONFIGURATION

Here only database configuration is showed. In real application other configuration will take place here. For example while facebook or twitter api will be used, then the clientId and secrets also take place here.

config/index.js

**var **db = {
    *db: 'mainDatabsaeAddress',
    dbTest: 'testDatabaseAddress',
*    **mySecret**: **'Eminem'
**};

module.exports = {
    **database**: db
};

MODELS

mongoose is a prominent ODM(Object Data Model). Here a dummy database schema will be made and exported. For details please check the *doc*.

app/models/dummyModel/index.js

**var **mongoose = require(**'mongoose'**);
**var **Schema = mongoose.Schema;
mongoose.**Promise **= global.*Promise*;

**var **dummySchema = **new **Schema({
    **dummyValue**: { **type**: String, **Required**: **false**}
});
module.exports = mongoose.model(**'DummyModel'**, dummySchema);

CONTROLLERS

Here all the operation will be performed. If a client required a specific task to be done, then the router gonna make it confirmed here.

/app/controllers/dummyController/index.js

**var **base = process.**env**.PWD;
**var **dummyModel = require(base + **'/app/models/dummyModel'**);  *//if there is a database

***var ***dummyRoute *= (req, res) => {
    res.json(200, {
        **success**: **true**,
        **message**: **'Magic happens on port 3000'
    **});
};

module.exports = {
    *dummyRoute
*};

ROUTING

I just put one routing.

app/routes/endPoints/index.js

**var **base = process.**env**.PWD;
**var **express = require(**'express'**);
**var **router = express.Router();
**var **dummyController = require(base + **'/app/controllers/dummyController'**);

router.get(**'/'**, dummyController.*dummyRoute*);

module.exports = router;

TESTING

Here’s the testing helper method. Get used to the Test/utils.js

In the runner.js file the data will be available through this method. Using data we check the property and their values.

In app/controllers/dummyController/test/runner.js the basic routing will be tested. In this case the /api/

utils.js

**var ***responseValidatorAsync *= (expectedStatusCode, validationFunction) => {
    **return **{
        json: (statusCode, data) => {
            statusCode.should.equal(expectedStatusCode);
            validationFunction(data);
        },
        send: (statusCode, data) => {
            statusCode.should.equal(expectedStatusCode);
            validationFunction(data);
        }
    }
};
module.exports = {
    *responseValidatorAsync
*};

Here an expected status code and callBack method is invoked. In general error free status code is 200 and if there is an error 500, also 404 stands for the server not found. json and send is required the should.js assertion library.

runner.js

process.**env**.**NODE_ENV **= **'test'**;

var base = process.env.PWD;
var config = require(base + '/config');
var logger = require('mocha-logger');
var mongoose  = require('mongoose');
var should = require('should');
var testUtils = require(base + '/Test/utils');

var dummyController = require(base + '/app/controllers/dummyController');

describe('BOILERPLATE TESTING', () => {

    before((done) => {
        mongoose.connect(config.database.dbTest, (err, user) => {
            if(err) { logger.log('Error in connecting database: ' + err) }
            else { done(); }
        });
    });

    describe('TESTING DUMMY ROUTE', () => {
        it('should get a success message', (done) => {
            var req = {};
            var res = testUtils.responseValidatorAsync(200, (user) => {
                user.should.have.property('success');
                user.success.should.equal(true);
                done();
            });
            dummyController.dummyRoute(req, res);
        });
    });


    after((done) => {
        mongoose.disconnect(done);
    });
});

Testing is performed in the development phase. We set environment argument as the development.

Mainly here we test if the dummyRoute return a success message with a 200 status code and value ‘true’.

Before this case we connect the database and after the the testing is performed the database is disconnected.

To run the unit test

npm test

And VOILA

Now time to check if our dummyRoute /api/ is working… Open PostMan and send the request.

N.B. Before you test the api in postMan, you must run the application

nodemon
OR
node server.js
OR
npm start

There it is, ‘success’ is true and server running message is received.

CONCLUSION

Here I points the code, you will write every time for your application. Hope this be help-full. If there’s anything might be required, response below or create an issue in *Github*.

 
Share this