Node.js in Production

It is a partly sunny Sunday in the Twin Cities of Minneapolis and St. Paul. Currently I am waiting for my wife to get ready to go out for a walk. Yesterday it was cloudy and sprinkled on and off all day. Towards the end of the day it cleared up but we were not in the mood to walk given that we were watching movies at home.

Spoke on the phone with my youngest son. He and his wife were driving to Wisconsin from Indiana to check out a few homes today. He got a job in the area and the family is moving in a month or two. Seems like a house that matched their expectations came on the market last week and they are hoping will meet and exceed their expectations. Will find more about it later this afternoon when they drive back home.

Earlier this morning I read “You should never ever run directly against Node.js in production. Maybe.” by Burke Holland. Yes, I agree the article has a long title. While I was reading the article microservices and Kubernetes came to mind. Towards the end these subjects are brought up by the author.

I use node when I want to run something once.

C:\Users\John\express-demo>node index.js
      NODE_ENV: development
app.get('env'): development
Application Name: Storage Server - Development
     Mail Server: CONDOR
       Mail Port: 1111
storage server listening on port:  4444 ...
logging...
authenticating...
logging...
authenticating...
imageName ==>john.jpg<== fullPath ==>C:\Users\John\express-demo\public\john.jpg<==
exists: true
^C

I tend to use nodemon when I am writing code because I like to use the Test Driven Development (TDD) approach.

C:\Users\John\express-demo>nodemon index.js
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
      NODE_ENV: development
app.get('env'): development
Application Name: Storage Server - Development
     Mail Server: CONDOR
       Mail Port: 1111
storage server listening on port:  4444 ...
logging...
authenticating...
logging...
authenticating...
imageName ==>john.jpg<== fullPath ==>C:\Users\John\express-demo\public\john.jpg<==
exists: true
^CTerminate batch job (Y/N)? y

Burke covers the use of pm2 which is another process manager for Node.js applications. I decided to install it and give it a try.

C:\Users\John\express-demo>npm install pm2 -global
C:\Users\John\AppData\Roaming\npm\pm2-runtime -> C:\Users\John\AppData\Roaming\npm\node_modules\pm2\bin\pm2-runtime,
C:\Users\John\AppData\Roaming\npm\pm2-dev -> C:\Users\John\AppData\Roaming\npm\node_modules\pm2\bin\pm2-dev
C:\Users\John\AppData\Roaming\npm\pm2-docker -> C:\Users\John\AppData\Roaming\npm\node_modules\pm2\bin\pm2-docker
C:\Users\John\AppData\Roaming\npm\pm2 -> C:\Users\John\AppData\Roaming\npm\node_modules\pm2\bin\pm2
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.8 (node_modules\pm2\node_modules\fsevents):      ll: require-in-the-middle@3.0.0
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.8: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ pm2@3.5.0
added 310 packages from 258 contributors in 89.231s

I thought to run nodemon and pm2 in containers but that would have taken a little longer. If someone is interested I will oblige.

I used the following code to test nodemon and pm2. Both seem to operate as expected. That said; nodemon shows console messages while pm2 logs to files. Not sure if there is a flag or configuration to make pm2 write to a console. Based on the fact that you can run multiple copies of an app, it probably does not write to the console.

Following are a few lines of the log file generated by the server:

:::: :::: ::::
::1 - - [28/Apr/2019:15:39:07 +0000] "GET /api/images/john.jpg HTTP/1.1" 404 158
::1 - - [28/Apr/2019:15:39:28 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 200 345515
::1 - - [28/Apr/2019:15:40:01 +0000] "GET /api/images/name/john.xyz HTTP/1.1" 404 34
::1 - - [28/Apr/2019:15:49:54 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 200 345515
::1 - - [28/Apr/2019:15:50:31 +0000] "GET /api/images HTTP/1.1" 404 149
::1 - - [28/Apr/2019:15:50:38 +0000] "GET /api/images HTTP/1.1" 404 149
::1 - - [28/Apr/2019:15:50:47 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 200 345515
::1 - - [28/Apr/2019:15:50:55 +0000] "GET /api/images/name/john.xyz HTTP/1.1" 404 34
::1 - - [28/Apr/2019:15:51:04 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 304 -
::1 - - [28/Apr/2019:15:55:02 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 500 1238
::1 - - [28/Apr/2019:15:55:23 +0000] "GET /api/images HTTP/1.1" 404 149
::1 - - [28/Apr/2019:15:55:30 +0000] "GET /api/images/john.jpg HTTP/1.1" 404 158
::1 - - [28/Apr/2019:15:55:45 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 500 1238
::1 - - [28/Apr/2019:15:56:26 +0000] "GET /api/patients/ HTTP/1.1" 304 -
::1 - - [28/Apr/2019:15:57:13 +0000] "GET /api/images/name/john.jpg HTTP/1.1" 500 1238
:::: :::: ::::

Following is the JavaScript code I was using to test nodemon and pm2:

// **** load express framework ****
const express = require("express");

// **** use router ****
const router = express.Router();

// **** load fs module ****
const fs = require("fs");

// **** for validations (returns a class)****
const Joi = require("joi");

// **** for our custom logging ****
const logger = require("../middleware/logger.js");

// **** for our custom authentication ****
const authenticate = require("../middleware/authenticate.js");

// **** enable our logger middleware ****
router.use(logger);

// **** enable our authentication middleware ****
router.use(authenticate);

/*
 * route and handler (single image by name)
 */
router.get("/name/:name", (req, res) => {
  // **** get the image name ****
  let imageName = req.params.name;

  // ???? display the image name ????
  console.log(`imageName ==>${imageName}<==`); // // ???? create an issue ???? // imageName = "xxx.xxx"; // **** build full path to image **** const fullPath = "C:\\Users\\John\\express-demo\\public\\" + imageName; // ???? display the image full path ???? console.log(`fullPath ==>${fullPath}<==`); // **** check if image exists **** const exists = fs.existsSync(fullPath); // ???? display if the image exists ???? console.log(`exists: ${exists}`); // **** send response **** if (!exists) return res.status("404").send(`imageName ==>${imageName}<== NOT found`);
  // res.sendFile(fullPath);

  // ???? ????
  res.cake(fullPath);
});

// ***** export the router ****
module.exports = router;

I tried different things to make the application fail and crash.

First you need to start an application under control of pm2:

C:\Users\John\express-demo>pm2 start index.js

                        -------------

__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
 _\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
  _\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
   _\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
    _\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
     _\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
      _\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
       _\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
        _\///______________\///______________\///__\///////////////__


                          Runtime Edition

        PM2 is a Production Process Manager for Node.js applications
                     with a built-in Load Balancer.

                Start and Daemonize any application:
                $ pm2 start app.js

                Load Balance 4 instances of api.js:
                $ pm2 start api.js -i 4

                Monitor in production:
                $ pm2 monitor

                Make pm2 auto-boot at server restart:
                $ pm2 startup

                To go further checkout:
                http://pm2.io/


                        -------------

[PM2] Spawning PM2 daemon with pm2_home=C:\WINDOWS\system32\config\systemprofile\.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting C:\Users\John\express-demo\index.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬─────────┬──────┬───────┬────────┬─────────┬────────┬───────┬───────────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid   │ status │ restart │ uptime │ cpu   │ mem       │ user │ watching │
├──────────┼────┼─────────┼──────┼───────┼────────┼─────────┼────────┼───────┼───────────┼──────┼──────────┤
│ index    │ 0  │ 1.0.0   │ fork │ 12844 │ online │ 0       │ 1s     │ 40.7% │ 34.7 MB   │ John │ disabled │
└──────────┴────┴─────────┴──────┴───────┴────────┴─────────┴────────┴───────┴───────────┴──────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

You are able to list applications that are under control of pm2:

C:\Users\John\express-demo>pm2 list
┌──────────┬────┬─────────┬──────┬───────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid   │ status │ restart │ uptime │ cpu │ mem       │ user │ watching │
├──────────┼────┼─────────┼──────┼───────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ index    │ 0  │ 1.0.0   │ fork │ 12844 │ online │ 0       │ 2m     │ 0%  │ 31.0 MB   │ John │ disabled │
└──────────┴────┴─────────┴──────┴───────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

You are able to stop applications using pm2:

C:\Users\John\express-demo>pm2 stop 0
[PM2] Applying action stopProcessId on app [0](ids: 0)
[PM2] [index](0) ✓
┌──────────┬────┬─────────┬──────┬─────┬─────────┬─────────┬────────┬─────┬────────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid │ status  │ restart │ uptime │ cpu │ mem    │ user │ watching │
├──────────┼────┼─────────┼──────┼─────┼─────────┼─────────┼────────┼─────┼────────┼──────┼──────────┤
│ index    │ 0  │ 1.0.0   │ fork │ 0   │ stopped │ 0       │ 0      │ 0%  │ 0 B    │ John │ disabled │
└──────────┴────┴─────────┴──────┴─────┴─────────┴─────────┴────────┴─────┴────────┴──────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

An you are able to delete applications from pm2:

C:\Users\John\express-demo>pm2 delete 0
[PM2] Applying action deleteProcessId on app [0](ids: 0)
[PM2] [index](0) ✓
┌──────────┬────┬─────────┬──────┬─────┬────────┬─────────┬────────┬─────┬─────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
└──────────┴────┴─────────┴──────┴─────┴────────┴─────────┴────────┴─────┴─────┴──────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

After spending a couple hours experimenting with nodemon and pm2, I think that in early stages of development, I will stick to nodemon. I will start using pm2 for tests later in the development cycle and before switching to microservices and Kubernetes for production.

If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.