Writing a Production Ready Express Server like a Pro – Part 2

When it comes to a production server, testing forms a very important part, because even a small deviation from the desired output can cause a lot of damage!

Firstly to write tests for our project we create a tests folder, and then a folder for unit test and one for integration test within it.

tests
├── integration
└── unit

Setup –

I will be using Chai and Mocha libraries to write and run these tests.

Run the following command for necessary installations –
npm i chai @types/chai mocha ts-node typescript --dev

After the installations add the test script to package.json file –
 "test": "mocha -r ts-node/register tests/**/*.test.ts",

Additionally we also create an alias for the folder so it can be easily addressed as @src from anywhere in the tests folder.

Therefore the package.json now looks something like this –

{
    "name": "productionserver",
    "version": "1.0.0",
    "description": "A Production Ready Server",
    "main": "./build/index.js",
    "scripts": {
        "dev": "tsc-watch --onSuccess \"node ./dist/server.js\"",
        "test": "mocha -r ts-node/register tests/**/*.test.ts",
        "start": "ts-node index.ts"
    },
    "author": "Dhairya Gada",
    "license": "ISC",
    "dependencies": {
        "@types/express": "^4.17.3",
        "@types/winston": "^2.4.4",
        "cors": "^2.8.5",
        "express": "^4.17.1",
        "module-alias": "^2.2.2",
        "winston": "^3.2.1"
    },
    "_moduleAliases": {
        "@root": ".",
        "@src": "build/src",
        "@utils": "build/src/utils",
        "@configs": "build/configs"
    },
    "devDependencies": {
        "@types/chai": "^4.2.11",
        "chai": "^4.2.0",
        "mocha": "^7.1.1",
        "ts-node": "^8.8.2",
        "typescript": "^3.8.3"
    }
}

Unit Tests

Unit tests are written to test individual parts of the project.

Now lets write a test for the Hello World Class –

require ('module-alias/register')
import { expect } from "chai";
import { HelloWorld } from "@src/serviceclasses/helloworld/HelloWorld";

describe("Hello World Test", () => {

  it("It Should Return Hello World!", async () => {
    let result = await HelloWorld.wishHello()
    expect(result).to.equal("Hello World!")
  })
})

Note : Don’t forget to add require ('module-alias/register') since we are using Module Alias.

Basically this test checks if our wishHello() function in the HelloWorld class is returning the expected output. Though this is just for illustrating that how the tests work and might seem like a trivial test but when the functions and classes are carrying out complex tasks the probability of getting an unexpected output rises and this is where these tests help in pointing them out so that it can be addresses before the project goes live.

Integration Test

Integration Tests are written to test the end to end integration. Basically we will be testing the API endpoints. In a project with hundreds of API’s it is not possible to use postman and examine each and every API to know if they are functioning or not, that’s where these tests come in handy.

For checking API Endpoints we will be mocking the API calls with the library chai-http which can be installed by npm i chai-http @types/chai-http

Following is the test for Hello World Controller –

require('module-alias/register')
import { server } from "@src/core/Server";
import chai from "chai";
import { expect } from "chai";
import chaiHttp from "chai-http";
import { Express } from 'express'
import { StatusConstants } from "@src/constants/StatusConstants";
import { apserver } from "../../index";

chai.use(chaiHttp);

describe("Hello World API check", async() => {
     
    let app = await apserver

    it("It Should return a status 200 and Hello World! Message", done => {
        chai
            .request(app)
            .get("/helloworld")
            .end((err, res) => {
                expect(res).to.have.status(StatusConstants.code200);
                expect(res.text).to.equals("Hello World!")
                done();
            });
    });
})

If you recall I had written an Error Handling Middleware that returns a status 404 with the message Method Not Found whenever a route that isn’t define in the project is called. We will also be checking that route as follows –

require('module-alias/register')
import { server } from "@src/core/Server";
import chai from "chai";
import { expect } from "chai";
import chaiHttp from "chai-http";
import { Express } from 'express'
import { StatusConstants } from "@src/constants/StatusConstants";
import { apserver } from "../../index";

chai.use(chaiHttp);

describe("Non Existing API Error check", async () => {
    
    let app = await apserver

    it("It Should return a status 400", done => {
        chai
            .request(app)
            .get("/somepath")
            .end((err, res) => {
                expect(res).to.have.status(StatusConstants.code404);
                expect(res.text).to.equals(StatusConstants.code404Message);
                done();
            });
    });
})

Finally after writing these tests we can run them by the command npm run test, which gives us the following output after all the tests are passed –

Now for the final part,

Using a Process Manager –

When in production server we don’t simply npm run start because it is prone to crashes, hence we will be using a Process Manager.

The one that is most widely used and easiest is PM2. It should be installed globally as
npm i pm2 -g

After that it can be simply started off as pm2 start build/index.js from the root of the project. And that’s it, the server is up!

Let us call our API through Postman –

You can go through the documentation of PM2 to discover various other functionalities that it provides such as metrics and multiple instances.

That’s All Folks !

I hope that with these two articles you now have a fair idea of how the real world projects are organized.

In case you found these articles to be informative or helpful do leave a like on it, this encourages me a lot. Also, if you have any suggestions or any questions feel free to leave a comment below.

0

Leave a Reply