Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 361

Node.

JS

Yoni Goldberg
About Me – Yoni Goldberg

Backend. Only
Node. Only
Top 15 Node.JS projects on GitHub
Some Formalities Before We Take Off

Course flow
Prerequisites
Environment setup
Supporting materials
Questions (www.clarify.live)
Post course meeting
Intro

What is Node.JS?
It All
Began 8
Years
Ago…
2018 - 8 Years Later

8M 1st 2nd
Servers worldwide Most popular Most loved
framework technology
(StackOverflow) (StackOverlow)
Platforms Packages
Compared

200,000
Maven Java – 220,000
150,000
100,000 Nugget .NET - ~100,000

PyPi Python - ~200,000

page
07
Platforms Packages
Compared

720,000
NPM Node.JS – 596,000

Maven Java – 220,000

Nugget .NET - ~100,000


220,000
150,000 180,000
PyPi Python - ~200,000

page
08
What Is Node Secret Sauce?

“A language that doesn't affect the way you


think about programming, is not worth
knowing”

john doe
1. Node Secret Sauce - House Of Many Ideas

OOP vs Functional

Dynamic typing vs Strict typing

Adhoc and lightweight vs Robust architecture


2. Node Secret Sauce - Code once, deploy anywhere

Desktop IOT Smart Devices

Browser Web Server


3. Node Secret Sauce - Vibrant Community

726,000 packages

Re-inventing the wheel every year

Open source obsessive


4. Node Secret Sauce - Freedom

“We’re JavaScript developers.


Where we are going there are no
roads”
Agenda - The World Of Node.JS

Your Code + Community


Code
+ Node
Libraries

+ Service
Architecture + Web
Framework

+ Testing

+ Production Setup

+ Node Engine
Section

Development Basics
The Basic Pillars - JS
The Basic Pillars - Modules
Creating Modules

Module = file

Choose what to export

Exporting code -> module.exports = someFunction

Importing code -> require(‘./someModule.js’)


NPM – Node Package Manager

Solution = 10% your code, 90% others code

Describes properties and dependencies

Documented within package.json

Marketplace of components (720K!)


Hello-World Exercise

The Packing Advisor

Goal: Combine JS, NPM and modules (require)


Time: 20 minutes
Requirements:
Get input – City name
Output – whether we need to pack a coat or not
Logic – If the temperature will drop below 15 degree in the next 5 days - output that a coat is needed
Tech: use the NPM package "weather-js", encapsulate login in a Class
Starter file: ~/basics/the-packing-advisor/start.js
Section

Asynchronous Programming
Async Programming - Context

Node is a-synchronous by nature

Asynchronous programming is sometimes challenging

Mastering a-synchronous patterns is a must have skill for


Node developers
Synchronous Programming Flow
= Waiting & doing nothing
category one - Lorem
1. const options = {dbName: orders}
ipsum dlor sit amet //0.1ms
= Executing code
category four - =Lorem
2. const orders DB.getOrders(options)
ipsum dolor sit amet //8ms

3. logger.log(‘orders fetched’) //4ms

1% Executing
Synchronous Programming – Simple And Wasteful

Program is executed line by line

Blocks the thread while waiting

Requests are handled using multiple threads


A-Synchronous Programming Flow
= Waiting doing nothing
category one - Lorem
1. const options = {dbName: orders}
ipsum dlor sit amet //0.1ms
= Executing code
category four - =Lorem
2. const orders Don’t Wait
ipsum ->sitDB.getOrders(options)
dolor amet //0.1ms

3. Don’t Wait -> logger.log(‘orders fetched’) //0.1ms

We didn’t wait. But who will handle the result once it arrives?

100% Executing
A-Synchronous Programming – Efficient And Complex

Program execution’s flow can’t be predicted

Thread is never blocked

Node uses one thread which can handle ~10,000 requests per
second

Node.JS is single threaded, non-blocking


Async Techniques
Compared
Callback Promise Async Await

Nesting

Try-Catch

Debugging

Stacktrace

Synchronous loop

Asynchronous loop

Bottom Line
Option 1: Callback

The eldest paradigm (v0.1)

Main idea - Tell an async code/function which function to


invoke when it finishes

//Example: call some async function


searchOrders(‘USA’ , function(error, result){
console.log(‘This are the orders’ + result)
})
Async Demo Requirements

Get products from DB

Return products that a user ordered

Get input JSON with configuration – return in English or with local translation
Get current user id from DB
Fetch user orders
Based on orders, get all user products (translated or in English)
Return products. That’s it
Callback Exercise

The travelling recommender

Goal: Understand callbacks flow


Time: 25 minutes
Requirements:
Get input - traveling city, country & date
Output – whether it’s recommended to travel to the given city on the given date
Logic – If the given date falls on a vacation, print ‘No. The streets will be crowded!’
If the temperature might drop below 12 Celsius degree, print ‘Oh no, might be too cold’. Other wise print ‘Sure, go for it’
Bonus: get dates range, vacation start and end date and check for the entire period
Tech: use callbacks only, use the NPM packages: ‘weather-js’ & ‘date-holidays’
Async Techniques
Compared

Callback Promise Async Await

Nesting High

Try-Catch No

Debugging Tolerable

Stacktrace Named functions

Synchronous loop No

Asynchronous loop No

Bottom Line
Option 2: Promises

Grown by the community, now built-in

Main idea - the async code/function immediately provide


the caller with an object that will yield event when the
async operation completed

//Example: call some async function


let ordersPromise = searchOrders(‘USA’);
ordersPromise.then(function (result) {
console.log(‘This are the orders’ + result)
})
Promises Exercise

The travelling recommender

Goal: Understand promises flow


Time: 25 minutes
Requirements:
Get input - traveling city, country & date
Output – whether it’s recommended to travel to the given city on the given date
Logic – If the given date falls on a vacation, print ‘No. The streets will be crowded!’
If the temperature might drop below 12 Celsius degree, print ‘Oh no, might be too cold’. Other wise print ‘Sure, go for it’
Bonus: get dates range, vacation start and end date and check for the entire period
Tech: use promises only, use the NPM packages: ‘weather-js’ & ‘date-holidays’
Promises Exercise

The travelling recommender – quick version


Goal: Understand promises flow
Time: 25 minutes
Requirements:
Get input - traveling city, country
Output – whether it’s recommended to travel to the given city on the given date. If the given date falls on a vacation OR the
temperature might drop below 12 Celsius degree, print ‘No. The streets will be crowded!’ Other wise print ‘Sure, go for it’
Bonus: (a) Get multiple locations and check for each (b) Get dates range and check for the entire period
How:
Use promises only and return a promise. Get some help from the functions isWeatherWarmEnough (returns callback,
promisify it) and checkHolidays (return a promise)
Starter file: ~/async/travel-recommend-promises/start-with-helpers.js
Async Techniques
Compared

Callback Promise Async Await

Nesting High

Try-Catch No

Debugging Tolerable

Stacktrace Named functions

Synchronous loop No

Asynchronous loop No

Bottom Line
Option 3: Async/await

New, since v8.9

Main idea – synchronous programming alike!

//Example: call some async function


let result = await searchOrders(‘USA’);
console.log(‘This are the orders’ + result)
From Promises To
Async/await

function do(){ async function getOrders(){


new Promise((resolve, reject) =>{ const user = await logIn(“user”,
logIn("username", “p") “p”);
.then((user) => { const orders = await getOrders(user);
getOrders(user.name)})
.then((orders)=> { return orders
resolve(orders) }
}
})

}
Async/await Exercise

The travelling recommender

Goal: Understand async/await flow


Time: 25 minutes
Requirements:
Get input - traveling city, country & date
Output – whether it’s recommended to travel to the given city on the given date
Logic – If the given date falls on a vacation, print ‘No. The streets will be crowded!’
If the temperature might drop below 12 Celsius degree, print ‘Oh no, might be too cold’. Other wise print ‘Sure, go for it’
Bonus: get dates range, vacation start and end date and check for the entire period
Tech: use async/await only, use the NPM packages: ‘weather-js’ & ‘date-holidays’
A-Synchronous Programming Heaven

Async/await is your best friend – use it whenever possible

To run multiple functions simultaneously - use Promise.all

To avoid callbacks – Apply Util.Promisify over old packages


Section

Building REST API


The World Of Node.JS

Your Code + Community


Code
+ Node
Libraries

+ Node Engine Let’s


discuss
+ Web Application this now

+ Testing

+ Production Setup

= Great Solution
Anatomy Of Any Web App
Express Coverage

#must #quick-start
Express
Node.JS Framework
Popularity

18,679,0 Express – 18M downloads/day


60

KOA – 0.4M downloads/day

HAPI – 0.5M downloads/day

407,720 500,657 358,583 Restify – 0.35M downloads/day

page
047
What Express Brings To The Table

Simplification over Node’s built-in web server

Web request objects filled with properties and methods

Routing – invoke custom code per URL

Huge echo-system of utilities


Express
Middleware
What Are
Express Middleware
Middleware
Syntax

//1. define middleware func //2. Register


function(req, res, next){ app.use(middlewarefunc)
//custom code here
}
Middlewares & API Exercise

The rate limiter

Goal: Practice middleware programming


Time: 25 minutes
Requirements:
Get API input – write an API that receives a city name GET: /api/travel and returns whether it’s recommended for a vacation (based on the
exercise ‘The travel advisor’)
Logic for all incoming requests (all routes) – a specific user/ip can only request 4 requests per minute. If a visitor exceeds this limit - HTTP
status 429 should be returned and the request should not be processed
Logic for GET: /api/travel – if some parameter is missing, return HTTP status 400. Otherwise return HTTP status 200
Tech: Implement using ExpressJS, use middleware to limit the amount of requests
Starter file: ~/examples/the-rate-limiter/start.js
Express Exercise

The travel recommender API

Goal: Practice basic Express programming


Time: 25 minutes
Requirements:
Get API input – write an API that receives a city & country name GET: /api/travel and returns whether it’s recommended for a vacation (based
on the exercise ‘The travel advisor’).
Logic for all incoming requests (all routes) – if the request body is larger than 1000 characters length – return 400 http status
Bonus: (A) If some parameter is missing, return HTTP status 400. Otherwise return HTTP status 200. (B) A specific IP can not
perform more than 5 requests/sec
Tech: Implement using ExpressJS, use middleware to limit the amount of requests
Starter file: ~/examples/express/travel-recommender /start.js
Section

Project Structure
Inspiration

Warning: highly subjective topic

Based on the most popular paradigms:

• Domain Driven Design


• Microservices
• Clean Architecture
• Hexagonal Architecture
• Onion Architecture
Break Your App Into Small
Autonomous Components

Benefits:
Small is simple
Deploy with confidence
Resiliency
Tech agnostic
Granularly scalable
Moving From Spaghetti
To Ravioli
Microservices guidelines

#must #quick-start #nice-to-have #complex

1 Small and simple components 1


Deployed independently

2 Share no logic 2 Own a server or a container

3 Interaction via message queue (better) 3 Separate DB


or REST

4 Separate DB tables 4 Scale independently


Domain Driven Design (DDD)

#must #quick-start

Interest Over Time

2004 2018
DDD Core Principle -
Isolate The Domain
#must #quick-start

This is
where the
business
features live
DDD Layers In Node.JS

#must #quick-start

Express, etc

Custom code

NPM
Clean Architecture

#must #quick-start
“Heavy architecture” (Martin Fowler)

#must #quick-start
Practical Project Structure Guidelines

Project = single small microservice (+self contained)

Isolate the domain

Express != microservice

Express === Single entry point to your microservice

Layers = API, Domain: services, entities, data-access


Anatomy Of a Microservice

#must #quick-start
Anatomy Of a Microservice

#must #quick-start
Project Structure Demo
Section

NPM
NPM – Overview

Mashup architecture – 10% your code, 90% community

Has a competitor – Yarn (by Facebook)

Maintained by npm inc. company

640K packages available


NPM Typical Flow
NPM – Publishing a Package

Run mkdir ‘your-package-name’

npm init

Code it!

Add tests + readme.md + version

npm adduser

npm publish
Publishing NPM Package Exercise

Publish the ‘travelling recommender’

Goal: Understand package wrapping and life-cycle


Time: 20 minutes
Requirements:
Convert the ‘Travelling recommender’ exercise into NPM package and publish. Ensure to include some basic readme.m
Tech: Run ‘npm init’ to add package.json, ‘npm adduser’ & ‘npm publish’
Starter file: ~/examples/the-traveler/start.js
Lifecycle Of a Package

Publishers update packages constantly

When code is deployed in production newer dependency


version might be used

Lock dependencies to prevent drifting

Use ‘semver’ to understand package changes


Semver PATCH version when
you make backwards-
compatible bug fixes.

1.7.3
MAJOR version
when you make
incompatible API
changes

MINOR version when


you add functionality in
a backwards-
compatible manner
NPM – Popular Commands

npm init – craft an empty solution


npm install – add new local/online package
npm install <pkg> --save-dev – add package for dev purposes
npm uninstall <pkg> --save – remove a package
npm start – run the application
npm test – test the application
npm publish [tag] – share your app/package with the world
Section

Error Handling
The Ultimate Goals Of Error Handling

01 02 03
Visibility through Optimized API results Maximum uptime
operational dashboard
Error Handling Typical Flow
Throwing Errors
const user = await logIn("username",
"password");

if(!user){
throw new appError('userDoesntExist', 400,
'Couldnt find the user when trying to get
products', true);
}
Catchers

Local vs Global

Promise vs Async/await

Express caught exceptions

Uncaught exceptions
Error Handler

If(!err.isOperational){
process.exit(1)
}
Operational vs Developer Errors

Operational Non-trusted, Developer

Definition Run-time errors that the program can predict or reason Errors that the program can’t reason about their impact
about

Examples Invalid input, query timeout, 3rd party server not Compilation error, startup error, unknown error, errors
responding originated from ‘stateful’ objects

Typical scope Request Compilation, system-wide

Bottom Line Log and continue Log and exit


Error Handline Exercise

Protecting the ‘travelling recommender’

Goal: Practice Express error handling


Time: 20 minutes
Requirements:
Based on the ‘Travelling recommender’ exercise. If the given city doesn’t exist – return HTTP status 404.
Bonus: if there’s no internet connection (turn it off), the program won’t be able to access the weather service. In that case, return HTTP
status 500
Tech: Extend the build-in Error object with few additional properties. Use Express error-middleware.
Section

Authentication & Authorization


Challenges with
traditional auth flows
Auth flow
with JWT token
Middlewares & API Exercise

JWT Based Authentication

Goal: Understand how stateless authentication works


Time: 20 minutes
Requirements:
Login endpoint – receive username & password, if they are correct then return a signed JWT token with the user id and name. Otherwise, if
the credentials are wrong, return 404
Travel recommender endpoint – based on the existing API from previous exercises, prevent accessing to these API without valid token
Tech: Use the NPM package ‘jsonwebtoken’ to sign and verify tokens. Use a middleware to authenticate requests
Starter file: ~/examples/express-jwt/start.js
Section

Node Mechanics
Why Should I Care About
How Node Works?

01 02
When future code will run? When does my code become CPU-
Differences between various API:
setTimeout vs setImmediate vs
intensive?
callback, etc How sensitive is Node to synchronous
code?

03
Should I cluster?
Use one Node process or multiple?
The first thing
About Node.JS mechanics

Single Threaded
Non-Blocking
The first thing
About Node.JS mechanics

Single Threaded
Non-Blocking
Node.JS
Building Blocks

App Code Libuv Kernel


JS IO
01
Thread
s
V8

JS Engine
02 03 04
Node.JS
Building Blocks

App Code Libuv


“a magical place filled with unicorns and
Kernel
rainbows, and is the reason Node can
essentially be "single threaded" while
JS still allowing an arbitrary number of
IO
01 operations to be handled in the
background”
john doe
Thread
s
V8

JS Engine
02 03 04
Node.JS Flags:
Building Blocks --POOL_SIZE

App Code Libuv Kernel


JS IO
01 Flags:
--inspect
--use_strict
Thread
--many_other s
V8

JS Engine
02 03 04
4 Facts About
V8

01 02
Fast Single Threaded
Transform JS into machine code Runs using one thread only

03 04
Replaceable Sometimes not fast enough
Other vendors aim to provide their own GC, dynamic typing, etc – all come with a
implementation price

page
095
Typical Program Flow

Demonstrating
Node internals with
a real code
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate()
saveUser()

console.log(‘end’)
Main

Console Task Queue


Timer Network Immediate
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate() Console.log
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
Timer Network Immediate
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
Timer Network Immediate
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate() Validate()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
validate Timer Network Immediate
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
validate Timer Network Immediate
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate() saveUser()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
validate Timer Network Immediate
save
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
validate Timer Network Immediate
save
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate() Console.log()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
validate Timer Network Immediate
save
end
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate()
saveUser()

console.log(‘end’)
Main

Console Task Queue


starting
validate Timer Network Immediate
save
end
Code V8 Stack Async API

console.log(‘starting’)

validate = () => {console.log(‘validate’)};

saveUser = () => {console.log(‘save’)};

validate()
saveUser()

console.log(‘end’)

Console Task Queue


starting
validate Timer Network Immediate
save
end
Code Stack Async API
DB.save()
console.log(‘starting’)

validate = () =>
{console.log(‘validate’);saveUser()}; DB.save()

saveUser = () => {console.log(‘save’); saveUser()


await DB.save(user, callback);} Another Request
Console.log
Validate()
Callback
validate()

console.log(‘end’) Main

Console Task Queue


starting
Timer IO Immediate
validate
save Callback
end
Take
Away

Avoid 2. Monitor The 3. Replicate


1. Event Lop Processes
Starvation
Minimize CPU Measure average Diagnose whether
intensive tasks; avoid queue time; multiple processes
process.nextTick; use yields better
async API performance

page
0108
Part 6

Continuous Quality
Testing & Inspecting
Continuous Integration Inspection Trends

Old school

Modern
Demo

Live demo of running Jenkins


declarative pipeline in Jenkins,
getting mini build dashboard in
Slack, complexity reports,
coverage reports, live code
analysis with Wallaby and more
CI Market – Flexible vs Easy

CircleCI GitLab

CodeShip Travis
B
Easy

AWS CodeBuild

A
Jenkins

Popularity
Flexible
Take Away

01 02
Simple CI? Easy! Everything Docker
Getting test, coverage and linting CI tools are built around dockers - plan
automation is now a breeze your application around Docker

03 04
Plenty Of Tools Developer Experience
The market is not short of rich analysis CI is merely a bot that should buy
tools, don’t miss your free lunch developers time to handle strategic issues

page
0113
Part 7

DevOps
Production High-level
Topology
thank you.
Appendix

Node.JS
Best Practices
Appendix

Part 1 – Project Structure


Practices
1.1 Structure your solution by components

TL;DR: The worst large applications pitfall is maintaining a huge code


base with hundreds of dependencies - such a monolith slows down
developers as they try to incorporate new features. Instead, partition your
code into components, each gets its own folder or a dedicated codebase,
and ensure that each unit is kept small and simple. Visit 'Read More'
below to see examples of correct project structure

Otherwise: When developers who code new features struggle to realize


the impact of their change and fear to break other dependant
components - deployments become slower and more risky. It's also
considered harder to scale-out when all the business units are not
separated
1.1 Structure your solution by components
One Paragraph Explainer
For medium sized apps and above, monoliths are really bad - having one big software with many
dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects
— those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort
on design, and each change requires carefully evaluating the impact on other dependent objects.
The ultimate solution is to develop small software: divide the whole stack into self-contained
components that don't share files with others, each constitutes very few files (e.g. API, service, data
access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices'
architecture — it's important to understand that microservices are not a spec which you must
follow, but rather a set of principles. You may adopt many principles into a full-blown microservices
architecture or adopt only a few. Both are good as long as you keep the software complexity low.
The very least you should do is create basic borders between components, assign a folder in your
project root for each business component and make it self-contained - other components are
allowed to consume its functionality only through its public interface or API. This is the foundation
for keeping your components simple, avoid dependency hell and pave the way to full-blown
microservices in the future once your app grows.
"Scaling requires scaling of the entire application"

“Monolithic applications can be successful, but increasingly people are feeling


frustrations with them - especially as more applications are being deployed to
the cloud. Change cycles are tied together - a change made to a small part of the
application requires the entire monolith to be rebuilt and deployed. Over time it's
often hard to keep a good modular structure, making it harder to keep changes
that ought to only affect one module within that module. Scaling requires scaling
of the entire application rather than parts of it that require greater resource.”

From the blog,


MartinFowler.com
"So what does the architecture of your application scream?"

“...if you were looking at the architecture of a library, you’d likely see a
grand entrance, an area for check-in-out clerks, reading areas, small
conference rooms, and gallery after gallery capable of holding
bookshelves for all the books in the library. That architecture would
scream: Library.”

From the blog, 


uncle-bob
1.1 Structure your solution by components

Good:
Structure your
solution by
self-contained
components
1.1 Structure your solution by components

Bad: Group
your files by
technical
role
1.2 Layer your components, keep Express within its
boundaries

TL;DR: Each component should contain 'layers' - a dedicated object for the


web, logic and data access code. This not only draws a clean separation of
concerns but also significantly eases mocking and testing the system.
Though this is a very common pattern, API developers tend to mix layers by
passing the web layer objects (Express req, res) to business logic and data
layers - this makes your application dependant on and accessible by
Express only

Otherwise: App that mixes web objects with other layers can not be
accessed by testing code, CRON jobs and other non-Express callers
1.2 Layer your components, keep Express within its
boundaries

Separate
component
code into
layers: web,
services, and
DAL
1.3 Wrap common utilities as NPM packages

TL;DR: In a large app that constitutes a large code base, cross-


cutting-concern utilities like logger, encryption and alike, should be
wrapped by your own code and exposed as private NPM packages.
This allows sharing them among multiple code bases and projects

Otherwise: You'll have to invent your own deployment and


dependency wheel
1.3 Wrap common utilities as NPM packages
One Paragraph Explainer

Once you start growing and have different components on different servers
which consumes similar utilities, you should start managing the
dependencies - how can you keep 1 copy of your utility code and let
multiple consumer components use and deploy it? well, there is a tool for
that, it's called npm... Start by wrapping 3rd party utility packages with your
own code to make it easily replaceable in the future and publish your own
code as private npm package. Now, all your code base can import that code
and benefit free dependency management tool. It's possible to publish npm
packages for your own private use without sharing it publicly using 
private modules, private registry or local npm packages
1.3 Wrap common utilities as NPM packages

Sharing your
own common
utilities across
environments
and
components
1.4 Separate Express 'app' and 'server'

TL;DR: Avoid the nasty habit of defining the entire Express app in a single huge


file - separate your 'Express' definition to at least two files: the API declaration
(app.js) and the networking concerns (WWW). For even better structure, locate
your API declaration within components

Otherwise: Your API will be accessible for testing via HTTP calls only (slower
and much harder to generate coverage reports). It probably won't be a big
pleasure to maintain hundreds of lines of code in a single file
1.4 Separate Express 'app' and 'server'

One Paragraph Explainer

The latest Express generator comes with a great practice that is worth to
keep - the API declaration is separated from the network related
configuration (port, protocol, etc). This allows testing the API in-process,
without performing network calls, with all the benefits that it brings to the
table: fast testing execution and getting coverage metrics of the code. It also
allows deploying the same API under flexible and different network
conditions. Bonus: better separation of concerns and cleaner code
Code example: API declaration, should reside in app.js

var app = express();


app.use(bodyParser.json());
app.use("/api/events", events.API);
app.use("/api/forms", forms);
Code example: Server network declaration, should reside in
/bin/www

var app = require('../app');


var http = require('http');

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');


app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);


Example: test your API in-process using supertest (popular
testing package)

const app = express();

app.get('/user', function(req, res) {


res.status(200).json({ name: 'tobi' });
});

request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});
1.5 Use environment aware, secure and hierarchical config

TL;DR: A perfect and flawless configuration setup should


ensure (a) keys can be read from file AND from
environment variable (b) secrets are kept outside
committed code (c) config is hierarchical for easier
findability. There are a few packages that can help tick
most of those boxes like rc, nconf and config
Otherwise: Failing to satisfy any of the config
requirements will simply bog down the development or
devops team. Probably both
1.5 Use environment aware, secure and hierarchical config

One Paragraph Explainer


When dealing with configuration data, many things can just annoy and slow down:
1.setting all the keys using process environment variables becomes very tedious when in need to inject 100 keys
(instead of just committing those in a config file), however when dealing with files only the DevOps admins cannot
alter the behavior without changing the code. A reliable config solution must combine both configuration files +
overrides from the process variables
2.when specifying all keys in a flat JSON, it becomes frustrating to find and modify entries when the list grows bigger.
A hierarchical JSON file that is grouped into sections can overcome this issue + few config libraries allow to store the
configuration in multiple files and take care to union all at runtime. See example below
3.storing sensitive information like DB password is obviously not recommended but no quick and handy solution
exists for this challenge. Some configuration libraries allow to encrypt files, others encrypt those entries during GIT
commits or simply don't store real values for those entries and specify the actual value during deployment via
environment variables.
4.some advanced configuration scenarios demand to inject configuration values via command line (vargs) or sync
configuration info via a centralized cache like Redis so multiple servers will use the same configuration data.
Some configuration libraries can provide most of these features for free, have a look at NPM libraries like rc, nconf
 and configwhich tick many of these requirements.
Code Example – hierarchical config helps to find entries and
maintain huge config files
{
// Customer module configs
"Customer": {
"dbConfig": {
"host": "localhost",
"port": 5984,
"dbName": "customers"
},
"credit": {
"initialLimit": 100,
// Set low for development
"initialDays": 1
}
}
}
Part 2 – Error Handling
Practices
2.1 Use Async-Await or promises for async error handling

TL;DR: Handling async errors in callback style is probably the fastest


way to hell (a.k.a the pyramid of doom). The best gift you can give to
your code is using a reputable promise library or async-await instead
which enables a much more compact and familiar code syntax like try-
catch

Otherwise: Node.js callback style, function(err, response), is a


promising way to un-maintainable code due to the mix of error handling
with casual code, excessive nesting and awkward coding patterns
2.1 Use Async-Await or promises for async error handling

One Paragraph Explainer


Callbacks don’t scale well since most programmers are not familiar with them. They
force to check errors all over, deal with nasty code nesting and make it difficult to
reason about the code flow. Promise libraries like BlueBird, async, and Q pack a
standard code style using RETURN and THROW to control the program flow.
Specifically, they support the favorite try-catch error handling style which allows
freeing the main code path from dealing with errors in every function
Code Example – using promises to catch errors

doWork()
.then(doWork)
.then(doOtherWork)
.then((result) => doWork)
.catch((error) => {throw error;})
.then(verify);
Anti pattern code example – callback style error handling

getData(someParameter, function(err, result) {


if(err !== null) {
// do something like calling the given callback function and pass the error
getMoreData(a, function(err, result) {
if(err !== null) {
// do something like calling the given callback function and pass the error
getMoreData(b, function(c) {
getMoreData(d, function(e) {
if(err !== null ) {
// you get the idea? 
}
})
});
}
});
}
});
"We have a problem with promises"

“……And in fact, callbacks do something even more sinister: they deprive us of the stack,
which is something we usually take for granted in programming languages. Writing code
without a stack is a lot like driving a car without a brake pedal: you don’t realize how badly
you need it until you reach for it and it’s not there. The whole point of promises is to give
us back the language fundamentals we lost when we went async: return, throw, and the
stack. But you have to know how to use promises correctly in order to take advantage of
them.”

From the blog


pouchdb.com
"The promises method is much more compact"

“………The promises method is much more compact, clearer and quicker to


write. If an error or exception occurs within any of the ops it is handled by
the single .catch() handler. Having this single place to handle all errors
means you don’t need to write error checking for each stage of the work.”

From the blog


gosquared.com
"Promises are native ES6, can be used with generators"

“….Callbacks have a lousy error-handling story. Promises are better. Marry


the built-in error handling in Express with promises and significantly lower
the chances of an uncaught exception. Promises are native ES6, can be
used with generators, and ES7 proposals like async/await through
compilers like Babel”

From the blog


StrongLoop
"All those regular flow control constructs you are used to are
completely broken"

“……One of the best things about asynchronous, callback-based programming is


that basically all those regular flow control constructs you are used to are
completely broken. However, the one I find most broken is the handling of
exceptions. Javascript provides a fairly familiar try…catch construct for dealing
with exceptions. The problem with exceptions is that they provide a great way of
short-cutting errors up a call stack, but end up being completely useless of the
error happens on a different stack…”
From the blog
Benno’s
2.2 Use only the built-in Error object

TL;DR: Many throws errors as a string or as some custom type – this


complicates the error handling logic and the interoperability between modules.
Whether you reject a promise, throw an exception or an emit error – using only
the built-in Error object will increase uniformity and prevent loss of information

Otherwise: When invoking some component, being uncertain which type of errors


come in return – it makes proper error handling much harder. Even worse, using
custom types to describe errors might lead to loss of critical error information
like the stack trace!
2.2 Use only the built-in Error object
One Paragraph Explainer
The permissive nature of JS along with its variety code-flow options (e.g.
EventEmitter, Callbacks, Promises, etc) pushes to great variance in how
developers raise errors – some use strings, other define their own custom
types. Using Node.js built-in Error object helps to keep uniformity within your
code and with 3rd party libraries, it also preserves significant information
like the StackTrace. When raising the exception, it’s usually a good practice
to fill it with additional contextual properties like the error name and the
associated HTTP error code. To achieve this uniformity and practices,
consider extending the Error object with additional properties, see code
example below
Code Example – doing it right

// throwing an Error from typical function, whether sync or async


if(!productToAdd)
throw new Error("How can I add new product when no value provided?");

// 'throwing' an Error from EventEmitter


const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

// 'throwing' an Error from a Promise


return new Promise(function (resolve, reject) {
return DAL.getProduct(productToAdd.id).then((existingProduct) => {
if(existingProduct != null)
reject(new Error("Why fooling us and trying to add an existing product?"));
});
});
Code example – Anti Pattern

// throwing a string lacks any stack trace information and other important data properties
if(!productToAdd)
throw ("How can I add new product when no value provided?");
Code example – doing it even better

// centralized error object that derives from Node’s Error


function appError(name, httpCode, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.name = name;
//...other properties assigned here
};

appError.prototype.__proto__ = Error.prototype;

module.exports.appError = appError;

// client throwing an exception


if(user == null)
throw new appError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
"I don’t see the value in having lots of different types"

“…Personally, I don’t see the value in having lots of different types of error
objects – JavaScript, as a language, doesn’t seem to cater to Constructor-
based error-catching. As such, differentiating on an object property seems far
easier than differentiating on a Constructor type…”

From the blog,


Ben Nadel ranked 5 for the keywords “Node.js error object”
"A string is not an error"

“…passing a string instead of an error results in reduced interoperability between


modules. It breaks contracts with APIs that might be performing instanceof Error checks,
or that want to know more about the error. Error objects, as we’ll see, have very
interesting properties in modern JavaScript engines besides holding the message passed
to the constructor…”

From the blog,


devthought.com ranked 6 for the keywords “Node.js error object”
"Inheriting from Error doesn’t add too much value"

“…One problem that I have with the Error class is that is not so simple to extend. Of course,
you can inherit the class and create your own Error classes like HttpError, DbError, etc.
However, that takes time and doesn’t add too much value unless you are doing something
with types. Sometimes, you just want to add a message and keep the inner error, and
sometimes you might want to extend the error with parameters, and such…”

From the blog


machadogj
"All JavaScript and System errors raised by Node.js inherit from
Error"

“…All JavaScript and System errors raised by Node.js inherit from, or are instances of, the standard JavaScript
Error class and are guaranteed to provide at least the properties available on that class. A generic JavaScript
Error object that does not denote any specific circumstance of why the error occurred. Error objects capture a
“stack trace” detailing the point in the code at which the Error was instantiated, and may provide a text
description of the error. All errors generated by Node.js, including all System and JavaScript errors, will either be
instances of or inherit from, the Error class…”

From
Node.js official documentation
2.3 Distinguish operational vs programmer errors

TL;DR: Operational errors (e.g. API received an invalid input) refer to known


cases where the error impact is fully understood and can be handled
thoughtfully. On the other hand, programmer error (e.g. trying to read undefined
variable) refers to unknown code failures that dictate to gracefully restart the
application

Otherwise: You may always restart the application when an error appears, but
why let ~5000 online users down because of a minor, predicted, operational
error? the opposite is also not ideal – keeping the application up when an
unknown issue (programmer error) occurred might lead to an unpredicted
behavior. Differentiating the two allows acting tactfully and applying a balanced
approach based on the given context
2.3 Distinguish operational vs programmer errors

One Paragraph Explainer


Distinguishing the following two error types will minimize your app downtime
and helps avoid crazy bugs: Operational errors refer to situations where you
understand what happened and the impact of it – for example, a query to
some HTTP service failed due to connection problem. On the other hand,
programmer errors refer to cases where you have no idea why and
sometimes where an error came from – it might be some code that tried to
read an undefined value or DB connection pool that leaks memory.
Operational errors are relatively easy to handle – usually logging the error is
enough. Things become hairy when a programmer error pops up, the
application might be in an inconsistent state and there’s nothing better you
can do than to restart gracefully
Code Example – marking an error as operational (trusted)

// marking an error object as operational


var myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;

// or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
function appError(commonType, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.commonType = commonType;
this.description = description;
this.isOperational = isOperational;
};

throw new appError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);


"Programmer errors are bugs in the program"

“…The best way to recover from programmer errors is to crash immediately. You should run your
programs using a restarter that will automatically restart the program in the event of a crash. With a
restarter in place, crashing is the fastest way to restore reliable service in the face of a transient
programmer error…”

From the blog,


Joyent ranked 1 for the keywords “Node.js error handling”
"No safe way to leave without creating some undefined
brittle state"

“…By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick
up where you left off”, without leaking references, or creating some other sort of undefined brittle
state. The safest way to respond to a thrown error is to shut down the process. Of course, in a
normal web server, you might have many connections open, and it is not reasonable to abruptly shut
those down because an error was triggered by someone else. The better approach is to send an
error response to the request that triggered the error while letting the others finish in their normal
time, and stop listening for new requests in that worker.”

From
Node.js official documentation
"Otherwise you risk the state of your application"

“…So, unless you really know what you are doing, you should perform a graceful
restart of your service after receiving an “uncaughtException” exception event.
Otherwise, you risk the state of your application, or that of 3rd party libraries to
become inconsistent, leading to all kinds of crazy bugs…”

From the blog,


debugable.com ranked 3 for the keywords “Node.js uncaught exception”
“There are three schools of thoughts on error handling”

…There are primarily three schools of thoughts on error handling:

1. Let the application crash and restart it.


2. Handle all possible errors and never crash.
3. A balanced approach between the two

From the blog:


JS Recipes
2.4 Handle errors centrally, not within an Express middleware

TL;DR: Error handling logic such as mail to admin and logging


should be encapsulated in a dedicated and centralized object
that all endpoints (e.g. Express middleware, cron jobs, unit-
testing) call when an error comes in

Otherwise: Not handling errors within a single place will lead to


code duplication and probably to improperly handled errors
2.4 Handle errors centrally, not within an Express
middleware

One Paragraph Explainer


Without one dedicated object for error handling, greater are the chances of
important errors hiding under the radar due to improper handling. The error
handler object is responsible for making the error visible, for example by writing
to a well-formatted logger, sending events to some monitoring product or to an
admin directly via email. A typical error handling flow might be: Some module
throws an error -> API router catches the error -> it propagates the error to the
middleware (e.g. Express, KOA) who is responsible for catching errors -> a
centralized error handler is called -> the middleware is being told whether this
error is an untrusted error (not operational) so it can restart the app gracefully.
Note that it’s a common, yet wrong, practice to handle errors within Express
middleware – doing so will not cover errors that are thrown in non-web interfaces
Code Example – a typical error flow

// DAL layer, we don't handle errors here


DB.addDocument(newCustomer, (error, result) => {
if (error)
throw new Error("Great error explanation comes here", other useful parameters)
});

// API route code, we catch both sync and async errors and forward to the middleware
try {
customerService.addNew(req.body).then((result) => {
res.status(200).json(result);
}).catch((error) => {
next(error)
});
}
catch (error) {
next(error);
}

// Error handling middleware, we delegate the handling to the centralized error handler
app.use((err, req, res, next) => {
errorHandler.handleError(err).then((isOperationalError) => {
if (!isOperationalError)
next(err);
});
});
Code example – handling errors within a dedicated object

module.exports.handler = new errorHandler();

function errorHandler(){
this.handleError = function (error) {
return
logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError)
;
}
}
Code Example – Anti Pattern: handling errors within the
middleware

// middleware handling the error directly, who will handle Cron jobs and testing errors?
app.use((err, req, res, next) => {
logger.logError(err);
if(err.severity == errors.high)
mailer.sendMail(configuration.adminMail, "Critical error occured", err);
if(!err.isOperational)
next(err);
});
"Sometimes lower levels can’t do anything useful except
propagate the error to their caller"

“…You may end up handling the same error at several levels of the stack. This happens when lower levels can’t do
anything useful except propagate the error to their caller, which propagates the error to its caller, and so on. Often,
only the top-level caller knows what the appropriate response is, whether that’s to retry the operation, report an
error to the user, or something else. But that doesn’t mean you should try to report all errors to a single top-level
callback, because that callback itself can’t know in what context the error occurred…”

From the blog


Joyent, ranked 1 for the keywords “Node.js error handling”
"Handling each err individually would result in tremendous
duplication"

“……In Hackathon Starter api.js controller alone, there are over 79


occurrences of error objects. Handling each err individually would result
in a tremendous amount of code duplication. The next best thing you
can do is to delegate all error handling logic to an Express
middleware…”

From the blog


JS Recipes ranked 17 for the keywords “Node.js error handling”
 "HTTP errors have no place in your database code"

“……You should set useful properties in error objects, but use such properties
consistently. And, don’t cross the streams: HTTP errors have no place in your
database code. Or for browser developers, Ajax errors have a place in the code
that talks to the server, but not code that processes Mustache templates…”

From the blog


Daily JS ranked 14 for the keywords “Node.js error handling”
2.5 Document API errors using Swagger

TL;DR: Let your API callers know which errors might come in return
so they can handle these thoughtfully without crashing. This is
usually done with REST API documentation frameworks like
Swagger

Otherwise: An API client might decide to crash and restart only


because he received back an error he couldn’t understand. Note:
the caller of your API might be you (very typical in a microservice
environment)
2.5 Document API errors using Swagger

One Paragraph Explainer


REST APIs return results using HTTP status codes, it’s absolutely
required for the API user to be aware not only about the API schema
but also about potential errors – the caller may then catch an error
and tactfully handle it. For example, your API documentation might
state in advance that HTTP status 409 is returned when the customer
name already exists (assuming the API register new users) so the
caller can correspondingly render the best UX for the given situation.
Swagger is a standard that defines the schema of API documentation
offering an eco-system of tools that allow creating documentation
easily online, see print screens below
"You have to tell your callers what errors can happen"

“We’ve talked about how to handle errors, but when you’re writing a
new function, how do you deliver errors to the code that called your
function? …If you don’t know what errors can happen or don’t know
what they mean, then your program cannot be correct except by
accident. So if you’re writing a new function, you have to tell your
callers what errors can happen and what they mean…”

From the blog


Joyent, ranked 1 for the keywords “Node.js logging”
2.6 Shut the process gracefully when a stranger comes to
town

TL;DR: When an unknown error occurs (a developer error, see best


practice number #3)- there is uncertainty about the application
healthiness. A common practice suggests restarting the process
carefully using a ‘restarter’ tool like Forever and PM2

Otherwise: When an unfamiliar exception is caught, some object


might be in a faulty state (e.g an event emitter which is used globally
and not firing events anymore due to some internal failure) and all
future requests might fail or behave crazily
2.6 Shut the process gracefully when a stranger comes to
town
One Paragraph Explainer
Somewhere within your code, an error handler object is responsible for
deciding how to proceed when an error is thrown – if the error is trusted (i.e.
operational error, see further explanation within best practice #3) then writing
to log file might be enough. Things get hairy if the error is not familiar – this
means that some component might be in a faulty state and all future
requests are subject to failure. For example, assuming a singleton, stateful
token issuer service that threw an exception and lost its state – from now it
might behave unexpectedly and cause all requests to fail. Under this
scenario, kill the process and use a ‘Restarter tool’ (like Forever, PM2, etc) to
start over with a clean slate.
Code example: deciding whether to crash

// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
errorManagement.handler.handleError(error);
if(!errorManagement.handler.isTrustedError(error))
process.exit(1)
});

// centralized error handler encapsulates error-handling related logic


function errorHandler() {
this.handleError = function (error) {
return logger.logError(err)
.then(sendMailToAdminIfCritical)
.then(saveInOpsQueueIfCritical)
.then(determineIfOperationalError);
}

this.isTrustedError = function (error) {


return error.isOperational;
}
}
"The best way is to crash"

…The best way to recover from programmer errors is to crash immediately.


You should run your programs using a restarter that will automatically
restart the program in the event of a crash. With a restarter in place, crashing
is the fastest way to restore reliable service in the face of a transient
programmer error…

From the blog


Joyent
"There are three schools of thoughts on error handling"

…There are primarily three schools of thoughts on error handling:

1. Let the application crash and restart it.


2. Handle all possible errors and never crash.
3. A balanced approach between the two

From the blog:


JS Recipes
"No safe way to leave without creating some undefined brittle
state"

“…By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick
up where you left off”, without leaking references, or creating some other sort of undefined brittle
state. The safest way to respond to a thrown error is to shut down the process. Of course, in a
normal web server, you might have many connections open, and it is not reasonable to abruptly shut
those down because an error was triggered by someone else. The better approach is to send an
error response to the request that triggered the error while letting the others finish in their normal
time, and stop listening for new requests in that worker.”

From
Node.js official documentation
2.7 Use a mature logger to increase error visibility

TL;DR: A set of mature logging tools like Winston, Bunyan or Log4J,


will speed-up error discovery and understanding. So forget about
console.log

Otherwise: Skimming through console.logs or manually through messy


text file without querying tools or a decent log viewer might keep you
busy at work until late
2.7 Use a mature logger to increase error visibility

One Paragraph Explainer


We all love console.log but obviously, a reputable and persistent logger like 
Winston, Bunyan (highly popular) or Pino (the new kid in town which is focused
on performance) is mandatory for serious projects. A set of practices and tools
will help to reason about errors much quicker – (1) log frequently using
different levels (debug, info, error), (2) when logging, provide contextual
information as JSON objects, see example below. (3) watch and filter logs using
a log querying API (built-in in most loggers) or a log viewer software (4) Expose
and curate log statement for the operation team using operational intelligence
tools like Splunk
Code Example – Winston Logger in action

// your centralized logger object


var logger = new winston.Logger({
level: 'info',
transports: [
new (winston.transports.Console)(),
new (winston.transports.File)({ filename: 'somefile.log' })
]
});

// custom code somewhere using the logger


logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });
Code Example – Querying the log folder (searching for entries)

var options = {
from: new Date - 24 * 60 * 60 * 1000,
until: new Date,
limit: 10,
start: 0,
order: 'desc',
fields: ['message']
};

// Find items logged between today and yesterday.


winston.query(options, function (err, results) {
// execute callback with results
});
"Logger Requirements"

Lets identify a few requirements (for a logger):

1. Timestamp each log line. This one is pretty self-explanatory – you should be able to tell when
each log entry occurred.
2. Logging format should be easily digestible by humans as well as machines.
3. Allows for multiple configurable destination streams. For example, you might be writing trace
logs to one file but when an error is encountered, write to the same file, then into error file and
send an email at the same time…

From the blog


Strong Loop
2.8 Test error flows using your favorite test framework
TL;DR: Whether professional automated QA or plain manual
developer testing – Ensure that your code not only satisfies positive
scenario but also handle and return the right errors. Testing
frameworks like Mocha & Chai can handle this easily (see code
examples within the "Gist popup")

Otherwise: Without testing, whether automatically or manually, you


can’t rely on our code to return the right errors. Without meaningful
errors – there’s no error handling
2.8 Test error flows using your favorite test framework

One Paragraph Explainer


Testing ‘happy’ paths is no better than testing failures. Good testing
code coverage demands to test exceptional paths. Otherwise, there
is no trust that exceptions are indeed handled correctly. Every unit
testing framework, like Mocha & Chai, supports exception testing
(code examples below). If you find it tedious to test every inner
function and exception you may settle with testing only REST API
HTTP errors.
Code example: ensuring the right exception is thrown using
Mocha & Chai

describe("Facebook chat", () => {


it("Notifies on new chat message", () => {
var chatService = new chatService();
chatService.participants = getDisconnectedParticipants();
expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
});
});
Code example: ensuring API returns the right HTTP error code

it("Creates new Facebook group", function (done) {


var invalidGroupInfo = {};
httpRequest({
method: 'POST',
uri: "facebook.com/api/groups",
resolveWithFullResponse: true,
body: invalidGroupInfo,
json: true
}).then((response) => {
// if we were to execute the code in this block, no error was thrown in the operation above
}).catch(function (response) {
expect(400).to.equal(response.statusCode);
done();
});
});
2.9 Discover errors and downtime using APM products

TL;DR: Monitoring and performance products (a.k.a APM)


proactively gauge your codebase or API so they can
automagically highlight errors, crashes and slow parts that you
were missing

Otherwise: You might spend great effort on measuring API


performance and downtimes, probably you’ll never be aware
which are your slowest code parts under real-world scenario and
how these affect the UX
2.9 Discover errors and downtime using APM products

One Paragraph Explainer


Exception != Error. Traditional error handling assumes the existence of Exception
but application errors might come in the form of slow code paths, API downtime,
lack of computational resources and more. This is where APM products come in
handy as they allow to detect a wide variety of ‘burried’ issues proactively with a
minimal setup. Among the common features of APM products are for example
alerting when the HTTP API returns errors, detect when the API response time
drops below some threshold, detection of ‘code smells’, features to monitor
server resources, operational intelligence dashboard with IT metrics and many
other useful features. Most vendors offer a free plan.
2.9 Discover errors and downtime using APM products

Wikipedia about APM

In the fields of information technology and systems management, Application


Performance Management (APM) is the monitoring and management of
performance and availability of software applications. APM strives to detect and
diagnose complex application performance problems to maintain an expected
level of service. APM is “the translation of IT metrics into business meaning ([i.e.]
value) Major products and segments
2.9 Discover errors and downtime using APM products
Understanding the APM marketplace
APM products constitute 3 major segments:
1.Website or API monitoring – external services that constantly monitor uptime and performance via HTTP requests.
Can be set up in few minutes. Following are few selected contenders: Pingdom, Uptime Robot, and New Relic
2.Code instrumentation – product family which requires embedding an agent within the application to use features like
slow code detection, exception statistics, performance monitoring and many more. Following are few selected
contenders: New Relic, App Dynamics
3.Operational intelligence dashboard – this line of products is focused on facilitating the ops team with metrics and
curated content that helps to easily stay on top of application performance. This usually involves aggregating multiple
sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are
few selected contenders: Datadog, Splunk, Zabbix
2.9 Discover errors and downtime using APM products

Example:
UpTimeRobot.Com –
Website monitoring
dashboard
2.9 Discover errors and downtime using APM products

Example:
AppDynamics.Com –
end to end monitoring
combined with code
instrumentation
 2.10 Catch unhandled promise rejections

TL;DR: Any exception thrown within a promise will get swallowed


and discarded unless a developer didn’t forget to explicitly handle.
Even if your code is subscribed to process.uncaughtException!
Overcome this by registering to the event
process.unhandledRejection

Otherwise: Your errors will get swallowed and leave no trace.


Nothing to worry about
 2.10 Catch unhandled promise rejections
One Paragraph Explainer
Typically, most of modern Node.js/Express application code runs within promises – whether
within the .then handler, a function callback or in a catch block. Surprisingly, unless a developer
remembered to add a .catch clause, errors thrown at these places are not handled by the
uncaughtException event-handler and disappear. Recent versions of Node added a warning
message when an unhandled rejection pops, though this might help to notice when things go
wrong but it's obviously not a proper error handling method. The straightforward solution is to
never forget adding .catch clauses within each promise chain call and redirect to a centralized
error handler. However, building your error handling strategy only on developer’s discipline is
somewhat fragile. Consequently, it’s highly recommended using a graceful fallback and
subscribe to process.on(‘unhandledRejection’, callback) – this will ensure that any promise error,
if not handled locally, will get its treatment.
Code example: these errors will not get caught by any error
handler (except unhandledRejection)

DAL.getUserById(1).then((johnSnow) => {
// this error will just vanish
if(johnSnow.isAlive == false)
throw new Error('ahhhh');
});
Code example: Catching unresolved and rejected promises

process.on('unhandledRejection', (reason, p) => {


// I just caught an unhandled promise rejection, since we already have fallback handler for unhandled errors (see below), let throw and let him handle that
throw reason;
});
process.on('uncaughtException', (error) => {
// I just received an error that was never handled, time to handle it and then decide whether a restart is needed
errorManagement.handler.handleError(error);
if (!errorManagement.handler.isTrustedError(error))
process.exit(1);
});
"If you can make a mistake, at some point you will"

Let’s test your understanding. Which of the following would


you expect to print an error to the console?

From the blog


James Nelson
"If you can make a mistake, at some point you will"

Promise.resolve(‘promised value’).then(() => {


throw new Error(‘error’);
});

Promise.reject(‘error value’).catch(() => {


throw new Error(‘error’);
});

new Promise((resolve, reject) => {


throw new Error(‘error’);
});
"If you can make a mistake, at some point you will"

I don’t know about you, but my answer is that I’d expect all of them to print an error.
However, the reality is that a number of modern JavaScript environments won’t
print errors for any of them.The problem with being human is that if you can make
a mistake, at some point you will. Keeping this in mind, it seems obvious that we
should design things in such a way that mistakes hurt as little as possible, and that
means handling errors by default, not discarding them.

From the blog


James Nelson
2.11 Fail fast, validate arguments using a dedicated library

TL;DR: This should be part of your Express best practices –


Assert API input to avoid nasty bugs that are much harder to track
later. The validation code is usually tedious unless you are using a
very cool helper library like Joi

Otherwise: Consider this – your function expects a numeric


argument “Discount” which the caller forgets to pass, later on,
your code checks if Discount!=0 (amount of allowed discount is
greater than zero), then it will allow the user to enjoy a discount.
OMG, what a nasty bug. Can you see it?
2.11 Fail fast, validate arguments using a
dedicated library
One Paragraph Explainer
We all know how checking arguments and failing fast is important to avoid
hidden bugs (see anti-pattern code example below). If not, read about explicit
programming and defensive programming. In reality, we tend to avoid it due to
the annoyance of coding it (e.g. think of validating hierarchical JSON object with
fields like email and dates) – libraries like Joi and Validator turn this tedious task
into a breeze.
2.11 Fail fast, validate arguments using a dedicated library

Wikipedia: Defensive Programming


Defensive programming is an approach to improve software and source code, in
terms of General quality – reducing the number of software bugs and problems.
Making the source code comprehensible – the source code should be readable
and understandable so it is approved in a code audit. Making the software
behave in a predictable manner despite unexpected inputs or user actions.
Code example: validating complex JSON input
using ‘Joi’

var memberSchema = Joi.object().keys({


password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
});

function addNewMember(newMember)
{
// assertions come first
Joi.assert(newMember, memberSchema); //throws if validation fails
// other logic here
}
Anti-pattern: no validation yields nasty bugs

// if the discount is positive let's then redirect the user to pring his discount coupons
function redirectToPrintDiscount(httpResponse, member, discount)
{
if(discount != 0)
httpResponse.redirect(`/discountPrintView/${member.id}`);
}

redirectToPrintDiscount(httpResponse, someMember);
// forgot to pass the parameter discount, why the heck was the user redirected to the discount screen?
"You should throw these errors immediately"

“A degenerate case is where someone calls an asynchronous function but doesn’t


pass a callback. You should throw these errors immediately since the program is
broken and the best chance of debugging it involves getting at least a stack trace
and ideally a core file at the point of the error. To do this, we recommend
validating the types of all arguments at the start of the function.”

From the blog:


Joyent
Appendix

Part 3 – Code Style Practices


3.1 Use ESLint

TL;DR: ESLint is the de-facto standard for checking possible code errors and
fixing code style, not only to identify nitty-gritty spacing issues but also to
detect serious code anti-patterns like developers throwing errors without
classification. Though ESLint can automatically fix code styles, other tools like 
prettier and beautify are more powerful in formatting the fix and work in
conjunction with ESLint

Otherwise: Developers will focus on tedious spacing and line-width concerns


and time might be wasted overthinking about the project's code style
3.2 Node.js Specific Plugins

TL;DR: On top of ESLint standard rules that cover vanilla JS only, add
Node-specific plugins like eslint-plugin-node, eslint-plugin-mocha and 
eslint-plugin-node-security

Otherwise: Many faulty Node.js code patterns might escape under the


radar. For example, developers might require(variableAsPath) files with a
variable given as path which allows attackers to execute any JS script.
Node.js linters can detect such patterns and complain early
3.3 Start a Codeblock's Curly Braces on the Same Line

TL;DR: The opening curly braces of a code block


should be in the same line of the opening
statement
Code Example

// Do
function someFunction() {
// code block
}

// Avoid
function someFunction()
{
// code block
}
3.4 Don't Forget the Semicolon

TL;DR: While not unanimously agreed upon, it is still recommended


to put a semicolon at the end of each statement. This will make your
code more readable and explicit to other developers who read it

Otherwise: As seen in the previous section, JavaScript's interpreter


automatically adds a semicolon at the end of a statement if there
isn't one which might lead to some undesired results
3.5 Name Your Functions

TL;DR: Name all functions, including closures and callbacks. Avoid


anonymous functions. This is especially useful when profiling a
node app. Naming all functions will allow you to easily understand
what you're looking at when checking a memory snapshot

Otherwise: Debugging production issues using a core dump


(memory snapshot) might become challenging as you notice
significant memory consumption from anonymous functions
3.6 Naming conventions for variables, constants, functions and
classes

TL;DR: Use lowerCamelCase when naming constants, variables and functions


and UpperCamelCase (capital first letter as well) when naming classes. This will
help you to easily distinguish between plain variables/functions, and classes that
require instantiation. Use descriptive names, but try to keep them short

Otherwise: Javascript is the only language in the world which allows invoking a


constructor ("Class") directly without instantiating it first. Consequently, Classes
and function-constructors are differentiated by starting with UpperCamelCase
Code Example

// for class name we use UpperCamelCase


class SomeClassExample {}

// for const names we use the const keyword and lowerCamelCase


const config = {
key: 'value'
};

// for variables and functions names we use lowerCamelCase


let someVariableExample = 'value';
function doSomething() {}
3.7 Prefer const over let. Ditch the var

TL;DR: Using const means that once a variable is assigned, it cannot be


reassigned. Preferring const will help you to not be tempted to use the same
variable for different uses, and make your code clearer. If a variable needs to be
reassigned, in a for loop, for example, use let to declare it. Another important
aspect of let is that a variable declared using it is only available in the block scope
in which it was defined. var is function scoped, not block scoped, and shouldn't be
used in ES6 now that you have const and let at your disposal

Otherwise: Debugging becomes way more cumbersome when following a variable


that frequently changes
3.8 Requires come first, and not inside functions

TL;DR: Require modules at the beginning of each file, before and outside of any
functions. This simple best practice will not only help you easily and quickly tell
the dependencies of a file right at the top but also avoids a couple of potential
problems

Otherwise: Requires are run synchronously by Node.js. If they are called from


within a function, it may block other requests from being handled at a more
critical time. Also, if a required module or any of its own dependencies throw an
error and crash the server, it is best to find out about it as soon as possible, which
might not be the case if that module is required from within a function
3.9 Do Require on the folders, not directly on the files

TL;DR: When developing a module/library in a folder, place an index.js


file that exposes the module's internals so every consumer will pass
through it. This serves as an 'interface' to your module and eases
future changes without breaking the contract

Otherwise: Changing the internal structure of files or the signature may


break the interface with clients
Code example

// Do
module.exports.SMSProvider = require('./SMSProvider');
module.exports.SMSNumberResolver = require('./SMSNumberResolver');

// Avoid
module.exports.SMSProvider = require('./SMSProvider/SMSProvider.js');
module.exports.SMSNumberResolver = require('./SMSNumberResolver/SMSNumberResolver.js');
3.10 Use the === operator

TL;DR: Prefer the strict equality operator === over the


weaker abstract equality operator ==. == will compare two
variables after converting them to a common type. There is
no type conversion in ===, and both variables must be of
the same type to be equal

Otherwise: Unequal variables might return true when


compared with the == operator
Code example

'' == '0' // false


0 == '' // true
0 == '0' // true

false == 'false' // false


false == '0' // true

false == undefined // false


false == null // false
null == undefined // true

' \t\r\n ' == 0 // true


3.11 Use Async Await, avoid callbacks

TL;DR: Node 8 LTS now has full support for Async-await. This is a new way of
dealing with asynchronous code which supersedes callbacks and promises.
Async-await is non-blocking, and it makes asynchronous code look synchronous.
The best gift you can give to your code is using async-await which provides a
much more compact and familiar code syntax like try-catch

Otherwise: Handling async errors in callback style is probably the fastest way to


hell - this style forces to check errors all over, deal with awkward code nesting and
make it difficult to reason about the code flow
3.12 Use Fat (=>) Arrow Functions

TL;DR: Though it's recommended to use async-await and avoid


function parameters when dealing with older API that accept promises
or callbacks - arrow functions make the code structure more compact
and keep the lexical context of the root function (i.e. 'this')

Otherwise: Longer code (in ES5 functions) is more prone to bugs and


cumbersome to read
Appendix

Part 4 – Testing And Overall


Quality Practices
4.1 At the very least, write API (component) testing
TL;DR: Most projects just don't have any automated testing due to
short timetables or often the 'testing project' run out of control and
being abandoned. For that reason, prioritize and start with API testing
which is the easiest to write and provide more coverage than unit
testing (you may even craft API tests without code using tools like 
Postman. Afterward, should you have more resources and time,
continue with advanced test types like unit testing, DB testing,
performance testing, etc

Otherwise: You may spend long days on writing unit tests to find out
that you got only 20% system coverage
4.2 Detect code issues with a linter
TL;DR: Use a code linter to check basic quality and detect anti-
patterns early. Run it before any test and add it as a pre-commit git-
hook to minimize the time needed to review and correct any issue.
Also check Section 3 on Code Style Practices

Otherwise: You may let pass some anti-pattern and possible


vulnerable code to your production environment.
4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs
Travis vs Rest of the world)
TL;DR: Your continuous integration platform (CICD) will host all the quality tools
(e.g test, lint) so it should come with a vibrant ecosystem of plugins. Jenkins
 used to be the default for many projects as it has the biggest community along
with a very powerful platform at the price of complex setup that demands a
steep learning curve. Nowadays, it became much easier to set up a CI solution
using SaaS tools like CircleCI and others. These tools allow crafting a flexible CI
pipeline without the burden of managing the whole infrastructure. Eventually, it's
a trade-off between robustness and speed - choose your side carefully

Otherwise: Choosing some niche vendor might get you blocked once you need
some advanced customization. On the other hand, going with Jenkins might
burn precious time on infrastructure setup
4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs
Travis vs Rest of the world)
One Paragraph Explainer
The CI world used to be the flexibility of Jenkins vs the simplicity of SaaS vendors.
The game is now changing as SaaS providers like CircleCI and Travis offer robust
solutions including Docker containers with minimum setup time while Jenkins tries
to compete on 'simplicity' segment as well. Though one can setup rich CI solution in
the cloud, should it required to control the finest details Jenkins is still the platform
of choice. The choice eventually boils down to which extent the CI process should be
customized: free and setup free cloud vendors allow to run custom shell commands,
custom docker images, adjust the workflow, run matrix builds and other rich
features. However, if controlling the infrastructure or programming the CI logic using
a formal programming language like Java is desired - Jenkins might still be the
choice. Otherwise, consider opting for the simple and setup free cloud option
Code Example – a typical cloud CI configuration. Single .yml file
and that's it
version: 2
jobs:
build:
docker:
- image: circleci/node:4.8.2
- image: mongo:3.4.4
steps:
- checkout
- run:
name: Install npm wee
command: npm install
test:
docker:
- image: circleci/node:4.8.2
- image: mongo:3.4.4
steps:
- checkout
- run:
name: Test
command: npm test
- run:
name: Generate code coverage
command: './node_modules/.bin/nyc report --reporter=text-lcov'
- store_artifacts:
path: coverage
prefix: coverage
4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs
Travis vs Rest of the world)

Circle CI -
almost zero
setup cloud
CI
4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs
Travis vs Rest of the world)

Jenkins -
sophisticated
and robust
CI
4.4 Constantly inspect for vulnerable dependencies
TL;DR: Even the most reputable dependencies such as
Express have known vulnerabilities. This can get easily
tamed using community and commercial tools such as 🔗 
nsp that can be invoked from your CI on every build

Otherwise: Keeping your code clean from vulnerabilities


without dedicated tools will require to constantly follow
online publications about new threats. Quite tedious
4.5 Tag your tests

TL;DR: Different tests must run on different scenarios: quick smoke, IO-less,


tests should run when a developer saves or commits a file, full end-to-end tests
usually run when a new pull request is submitted, etc. This can be achieved by
tagging tests with keywords like #cold #api #sanity so you can grep with your
testing harness and invoke the desired subset. For example, this is how you
would invoke only the sanity test group with Mocha: mocha --grep 'sanity‘

Otherwise: Running all the tests, including tests that perform dozens of DB


queries, any time a developer makes a small change can be extremely slow and
keeps developers away from running tests
4.6 Check your test coverage, it helps to identify wrong test
patterns

TL;DR: Code coverage tools like Istanbul/NYC are great for 3


reasons: it comes for free (no effort is required to benefit this
reports), it helps to identify a decrease in testing coverage, and last
but not least it highlights testing mismatches: by looking at colored
code coverage reports you may notice, for example, code areas that
are never tested like catch clauses (meaning that tests only invoke
the happy paths and not how the app behaves on errors). Set it to fail
builds if the coverage falls under a certain threshold

Otherwise: There won't be any automated metric telling you when a


large portion of your code is not covered by testing
4.7 Inspect for outdated packages
TL;DR: Use your preferred tool (e.g. 'npm outdated' or npm
-check-updates to detect installed packages which are outdated, inject
this check into your CI pipeline and even make a build fail in a severe
scenario. For example, a severe scenario might be when an installed
package is 5 patch commits behind (e.g. local version is 1.3.1 and
repository version is 1.3.8) or it is tagged as deprecated by its author -
kill the build and prevent deploying this version

Otherwise: Your production will run packages that have been explicitly


tagged by their author as risky
4.8 Use docker-compose for e2e testing

TL;DR: End to end (e2e) testing which includes live data used to be the weakest
link of the CI process as it depends on multiple heavy services like DB. Docker-
compose turns this problem into a breeze by crafting production-like environment
using a simple text file and easy commands. It allows crafting all the dependent
services, DB and isolated network for e2e testing. Last but not least, it can keep a
stateless environment that is invoked before each test suite and dies right after

Otherwise: Without docker-compose teams must maintain a testing DB for each


testing environment including developers machines, keep all those DBs in sync
so test results won't vary across environments
Appendix

Part 5 – Going to Production


Practices
5.1. Monitoring!

TL;DR: Monitoring is a game of finding out issues before customers


do – obviously this should be assigned unprecedented importance.
The market is overwhelmed with offers thus consider starting with
defining the basic metrics you must follow (my suggestions inside),
then go over additional fancy features and choose the solution that
ticks all boxes. Click ‘The Gist’ below for an overview of the solutions

Otherwise: Failure === disappointed customers. Simple


5.1. Monitoring!
One Paragraph Explainer
At the very basic level, monitoring means you can easily identify when bad things happen at production. For
example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy
your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that
must be watched to ensure a healthy state – CPU, server RAM, Node process RAM (less than 1.4GB), the
number of errors in the last minute, number of process restarts, average response time. Then go over some
advanced features you might fancy and add to your wish list. Some examples of a luxury monitoring feature:
DB profiling, cross-service measuring (i.e. measure business transaction), front-end integration, expose raw
data to custom BI clients, Slack notifications and many others.
Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog,
NewRelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are
hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward
tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. AWS CloudWatch, 
Google StackDriver) will tell you immediately about the hardware metrics but not about the internal app
behavior. On the other end, Log-based solutions such as ElasticSearch lack the hardware view by default. The
solution is to augment your choice with missing metrics, for example, a popular choice is sending application
logs to Elastic stack and configure some additional agent (e.g. Beat) to share hardware-related information to
get the full picture.
5.1. Monitoring!

Monitoring
example: AWS
cloudwatch default
dashboard. Hard to
extract in-app
metrics
5.1. Monitoring!

Monitoring
example:
StackDriver
default
dashboard. Hard
to extract in-app
metrics
5.1. Monitoring!

Monitoring
example:
Grafana as the
UI layer that
visualizes raw
data
“What Other Bloggers Say”

“…We recommend you to watch these signals for all of your services:
Error Rate: Because errors are user facing and immediately affect
your customers. Response time: Because the latency directly affects
your customers and business. Throughput: The traffic helps you to
understand the context of increased error rates and the latency too.
Saturation: It tells how “full” your service is. If the CPU usage is 90%,
can your system handle more traffic? …”

From the blog 


Rising Stack
5.2. Increase transparency using smart logging

TL;DR: Logs can be a dumb warehouse of debug statements or the


enabler of a beautiful dashboard that tells the story of your app. Plan
your logging platform from day 1: how logs are collected, stored and
analyzed to ensure that the desired information (e.g. error rate,
following an entire transaction through services and servers, etc) can
really be extracted

Otherwise: You end-up with a black box that is hard to reason about,


then you start re-writing all logging statements to add additional
information
5.2. Increase transparency using smart logging
One Paragraph Explainer
Since you print out log statements anyway and you're obviously in a need of some interface that wraps up
production information where you can trace errors and core metrics (e.g. how many errors happen every hour
and which is your slowest API end-point) why not invest some moderate effort in a robust logging framework
that will tick all boxes? Achieving that requires a thoughtful decision on three steps:
1. smart logging – at the bare minimum you need to use a reputable logging library like Winston, Bunyan and
write meaningful information at each transaction start and end. Consider to also format log statements as
JSON and provide all the contextual properties (e.g. user id, operation type, etc) so that the operations team can
act on those fields. Include also a unique transaction ID at each log line, for more information refer to the bullet
below “Write transaction-id to log”. One last point to consider is also including an agent that logs the system
resource like memory and CPU like Elastic Beat.
2. smart aggregation – once you have comprehensive information on your servers file system, it’s time to
periodically push these to a system that aggregates, facilities and visualizes this data. The Elastic stack, for
example, is a popular and free choice that offers all the components to aggregate and visualize data. Many
commercial products provide similar functionality only they greatly cut down the setup time and require no
hosting.
3. smart visualization – now the information is aggregated and searchable, one can be satisfied only with the
power of easily searching the logs but this can go much further without coding or spending much effort. We can
now show important operational metrics like error rate, average CPU throughout the day, how many new users
opted-in in the last hour and any other metric that helps to govern and improve our app
5.2. Increase transparency using smart logging

Visualization
Example: Kibana
(part of the Elastic
stack) facilitates
advanced
searching on log
content
5.2. Increase transparency using smart logging

Visualization
Example: Kibana
(part of the
Elastic stack)
visualizes data
based on logs
“Logger Requirements”

Lets identify a few requirements (for a logger):

1. Timestamp each log line. This one is pretty self-explanatory – you should be able
to tell when each log entry occurred.
2. Logging format should be easily digestible by humans as well as machines.
3. Allows for multiple configurable destination streams. For example, you might be
writing trace logs to one file but when an error is encountered, write to the same file,
then into error file and send an email at the same time…

From the blog 

Strong Loop
5.3. Delegate anything possible (e.g. gzip, SSL) to a
reverse proxy

TL;DR: Node is awfully bad at doing CPU intensive tasks like


gzipping, SSL termination, etc. You should use ‘real’ middleware
services like nginx, HAproxy or cloud vendor services instead

Otherwise: Your poor single thread will stay busy doing


infrastructural tasks instead of dealing with your application core
and performance will degrade accordingly
5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse
proxy
One Paragraph Explainer

It’s very tempting to cargo-cult Express and use its rich middleware offering for
networking related tasks like serving static files, gzip encoding, throttling
requests, SSL termination, etc. This is a performance kill due to its single
threaded model which will keep the CPU busy for long periods (Remember,
Node’s execution model is optimized for short tasks or async IO related tasks).
A better approach is to use a tool that expertise in networking tasks – the most
popular are nginx and HAproxy which are also used by the biggest cloud
vendors to lighten the incoming load on node.js processes.
Nginx Config Example – Using nginx to compress server
responses
# configure gzip compression
gzip on;
gzip_comp_level 6;
gzip_vary on;

# configure upstream
upstream myApplication {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 64;
}

#defining web server


server {
# configure server with ssl and error pages
listen 80;
listen 443 ssl;
ssl_certificate /some/location/sillyfacesociety.com.bundle.crt;
error_page 502 /errors/502.html;

# handling static content


location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico)
{
root /usr/local/silly_face_society/node/public;
access_log off;
expires max;
}
“What Other Bloggers Say”

“…It’s very easy to fall into this trap – You see a package like Express and think “Awesome! Let’s get started”
– you code away and you’ve got an application that does what you want. This is excellent and, to be honest,
you’ve won a lot of the battle. However, you will lose the war if you upload your app to a server and have it
listen on your HTTP port because you’ve forgotten a very crucial thing: Node is not a web server. As soon as
any volume of traffic starts to hit your application, you’ll notice that things start to go wrong: connections
are dropped, assets stop being served or, at the very worst, your server crashes. What you’re doing is
attempting to have Node deal with all of the complicated things that a proven web server does really well.
Why reinvent the wheel? This is just for one request, for one image and bearing in mind this is the memory
that your application could be used for important stuff like reading a database or handling complicated
logic; why would you cripple your application for the sake of convenience?”

From the blog 


Mubaloo
“What Other Bloggers Say”

“Although express.js has built-in static file handling through some connect
middleware, you should never use it. Nginx can do a much better job of handling
static files and can prevent requests for non-dynamic content from clogging our
node processes…”

From the blog 


Argteam
5.4. Lock dependencies
TL;DR: Your code must be identical across all environments, but amazingly
NPM lets dependencies drift across environments by default – when you install
packages at various environments it tries to fetch packages’ latest patch
version. Overcome this by using NPM config files, .npmrc, that tell each
environment to save the exact (not the latest) version of each package.
Alternatively, for finer grain control use NPM” shrinkwrap”. *Update: as of NPM5,
dependencies are locked by default. The new package manager in town, Yarn,
also got us covered by default

Otherwise: QA will thoroughly test the code and approve a version that will
behave differently at production. Even worse, different servers at the same
production cluster might run different code
5.4. Lock dependencies
One Paragraph Explainer

Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then
by default when you deploy to production NPM might fetch momentjs 2.1.5 which unfortunately
brings some new bugs to the table. Using NPM config files and the argument –save-exact=true
instructs NPM to refer to the exact same version that was installed so the next time you run npm
install (in production or within a Docker container you plan to ship forward for testing) the same
dependent version will be fetched. An alternative and popular approach is using a .shrinkwrap file
(easily generated using NPM) that states exactly which packages and versions should be installed
so no environment can get tempted to fetch newer versions than expected.

• Update: as of NPM 5, dependencies are locked automatically using .shrinkwrap. Yarn, an


emerging package manager, also locks down dependencies by default.
Code example: .npmrc file that instructs NPM to use exact
versions

// save this as .npmrc file on the project directory


save-exact:true
Code example: shrinkwrap.json file that distills the exact
dependency tree

{
"name": "A",
"dependencies": {
"B": {
"version": "0.0.1",
"dependencies": {
"C": {
"version": "0.1.0"
}
}
}
}
}
Code example: NPM 5 dependencies lock file – package.json

{
"name": "package-name",
"version": "1.0.0",
"lockfileVersion": 1,
"dependencies": {
"cacache": {
"version": "9.2.6",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz",
"integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg=="
},
"duplexify": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
"integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
"dependencies": {
"end-of-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
"integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4="
}
}
}
}
}
5.5. Guard process uptime using the right tool
TL;DR: The process must go on and get restarted upon
failures. For simple scenarios, ‘restarter’ tools like PM2 might
be enough but in today ‘dockerized’ world – a cluster
management tools should be considered as well

Otherwise: Running dozens of instances without a clear


strategy and too many tools together (cluster management,
docker, PM2) might lead to a DevOps chaos
5.5. Guard process uptime using the right tool

One Paragraph Explainer


At the base level, Node processes must be guarded and restarted upon failures. Simply put, for
small apps and those who don’t use containers – tools like PM2 are perfect as they bring
simplicity, restarting capabilities and also rich integration with Node. Others with strong Linux
skills might use systemd and run Node as a service. Things get more interesting for apps that
use Docker or any container technology since those are usually accompanied by cluster
management and orchestration tools (e.g. AWS ECS, Kubernetes, etc) that deploy, monitor and
heal containers. Having all those rich cluster management features including container restart,
why mess up with other tools like PM2? There’s no bulletproof answer. There are good reasons
to keep PM2 within containers (mostly its containers specific version pm2-docker) as the first
guarding tier – it’s much faster to restart a process and provide Node-specific features like
flagging to the code when the hosting container asks to gracefully restart. Other might choose
to avoid unnecessary layers. To conclude this write-up, no solution suits them all and getting to
know the options is the important thing
“What Other Bloggers Say”

“... In development, you started your app simply from the command line with node
server.js or something similar. But doing this in production is a recipe for disaster. If
the app crashes, it will be offline until you restart it. To ensure your app restarts if it
crashes, use a process manager. A process manager is a “container” for
applications that facilitate deployment, provides high availability, and enables you to
manage the application at runtime.”

From the 
Express Production Best Practices
“What Other Bloggers Say”

“... Understanding NodeJS Clustering in Docker-Land “Docker containers are


streamlined, lightweight virtual environments, designed to simplify processes to
their bare minimum. Processes that manage and coordinate their own resources
are no longer as valuable. Instead, management stacks like Kubernetes, Mesos,
and Cattle have popularized the concept that these resources should be
managed infrastructure-wide. CPU and memory resources are allocated by
“schedulers”, and network resources are managed by stack-provided load
balancers.”

From the Medium blog post 

Understanding Node Clustering


5.6. Utilize all CPU cores

TL;DR: At its basic form, a Node app runs on a single CPU core while all other are
left idling. It’s your duty to replicate the Node process and utilize all CPUs – For
small-medium apps you may use Node Cluster or PM2. For a larger app consider
replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment
scripts that are based on Linux init system (e.g. systemd)

Otherwise: Your app will likely utilize only 25% of its available resources(!) or
even less. Note that a typical server has 4 CPU cores or more, naive deployment
of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!)
5.6. Utilize all CPU cores

One Paragraph Explainer


It might not come as a surprise that in its basic form, Node runs over a single
thread=single process=single CPU. Paying for beefy hardware with 4 or 8 CPU
and utilizing only one sounds crazy, right? The quickest solution which fits
medium sized apps is using Node’s Cluster module which in 10 lines of code
spawns a process for each logical core and route requests between the
processes in a round-robin style. Even better, use PM2 which sugarcoats the
clustering module with a simple interface and cool monitoring UI. While this
solution works well for traditional applications, it might fall short for applications
that require top-notch performance and robust DevOps flow. For those advanced
use cases, consider replicating the NODE process using custom deployment
script and balancing using a specialized tool such as nginx or use a container
engine such as AWS ECS or Kubernetees that have advanced features for
deployment and replication of processes.
5.6. Utilize all CPU cores

Comparison:
Balancing using
Node’s cluster vs
nginx
“What Other Bloggers Say”

“... The second approach, Node clusters, should, in theory, give the
best performance. In practice, however, distribution tends to be very
unbalanced due to operating system scheduler vagaries. Loads have
been observed where over 70% of all connections ended up in just two
processes, out of a total of eight ...”

From the 
Node.js documentation
“What Other Bloggers Say”

“... Clustering is made possible with Node’s cluster module. This enables a master
process to spawn worker processes and distribute incoming connections among
the workers. However, rather than using this module directly, it’s far better to use
one of the many tools out there that do it for you automatically; for example node-
pm or cluster-service ...”

From the blog 

StrongLoop
“What Other Bloggers Say”

“... Node cluster is simple to implement and configure, things are kept
inside Node’s realm without depending on other software. Just
remember your master process will work almost as much as your
worker processes and with a little less request rate than the other
solutions ...”

From the Medium post 


Node.js process load balance performance: comparing
cluster module, iptables, and Nginx
 5.7. Create a ‘maintenance endpoint’

TL;DR: Expose a set of system-related information, like


memory usage and REPL, etc in a secured API. Although it’s
highly recommended to rely on standard and battle-tests
tools, some valuable information and operations are easier
done using code

Otherwise: You’ll find that you’re performing many “diagnostic


deploys” – shipping code to production only to extract some
information for diagnostic purposes
 5.7. Create a ‘maintenance endpoint’

One Paragraph Explainer


A maintenance endpoint is a highly secure HTTP API that is part of the app code and its purpose is to be
used by the ops/production team to monitor and expose maintenance functionality. For example, it can
return a heap dump (memory snapshot) of the process, report whether there are some memory leaks and
even allow to execute REPL commands directly. This endpoint is needed where the conventional DevOps
tools (monitoring products, logs, etc) fail to gather some specific type of information or you choose not to
buy/install such tools. The golden rule is using professional and external tools for monitoring and
maintaining the production, these are usually more robust and accurate. That said, there are likely to be
cases where the generic tools will fail to extract information that is specific to Node or to your app – for
example, should you wish to generate a memory snapshot at the moment GC completed a cycle – few npm
libraries will be glad to perform this for you but popular monitoring tools will likely miss this functionality. It is
important to keep this endpoint private and accessibly only by admins because it can become a target of a
DDOS attack.
Code example: generating a heap dump via code

const heapdump = require('heapdump');

// Check if request is authorized


function isAuthorized(req) {
// ...
}

router.get('/ops/heapdump', (req, res, next) => {


if (!isAuthorized(req)) {
return res.status(403).send('You are not authorized!');
}

logger.info('About to generate heapdump');

heapdump.writeSnapshot((err, filename) => {


console.log('heapdump file is ready to be sent to the caller', filename);
fs.readFile(filename, "utf-8", (err, data) => {
res.end(data);
});
});
});
5.8. Discover errors and downtime using APM products

TL;DR: Monitoring and performance products (a.k.a APM) proactively


gauge codebase and API so they can auto-magically go beyond
traditional monitoring and measure the overall user-experience
across services and tiers. For example, some APM products can
highlight a transaction that loads too slow on the end-users side
while suggesting the root cause

Otherwise: You might spend great effort on measuring API


performance and downtimes, probably you’ll never be aware which is
your slowest code parts under real-world scenario and how these
affects the UX
5.8. Discover errors and downtime using APM products

One Paragraph Explainer


APM (application performance monitoring) refers to a family of products that aims
to monitor application performance from end to end, also from the customer
perspective. While traditional monitoring solutions focus on Exceptions and
standalone technical metrics (e.g. error tracking, slow server endpoints, etc), in the
real world our app might create disappointed users without any code exceptions,
for example, if some middleware service performed real slow. APM products
measure the user experience from end to end, for example, given a system that
encompasses frontend UI and multiple distributed services – some APM products
can tell how fast a transaction that spans multiple tiers last. It can tell whether the
user experience is solid and point to the problem. This attractive offering comes
with a relatively high price tag hence it’s recommended for large-scale and complex
products that require going beyond straightforward monitoring.
5.8. Discover errors and downtime using APM products

APM example – a
commercial product
that visualizes cross-
service app
performance
5.8. Discover errors and downtime using APM products

APM example – a
commercial product
that emphasizes the
user experience
score
5.8. Discover errors and downtime using APM products

APM example – a
commercial
product that
highlights slow
code paths
5.9. Make your code production-ready

TL;DR: Code with the end in mind, plan for production from


day 1. This sounds a bit vague so I’ve compiled a few
development tips that are closely related to production
maintenance (click Gist below)

Otherwise: A world champion IT/DevOps guy won’t save a


system that is badly written
5.9. Make your code production-ready

One Paragraph Explainer


Following is a list of development tips that greatly affect the production maintenance and stability:
•The twelve-factor guide – Get familiar with the Twelve factors guide
•Be stateless – Save no data locally on a specific web server (see separate bullet – ‘Be Stateless’)
•Cache – Utilize cache heavily, yet never fail because of cache mismatch
•Test memory – gauge memory usage and leaks as part your development flow, tools such as ‘memwatch’ can greatly
facilitate this task
•Name functions – Minimize the usage of anonymous functions (i.e. inline callback) as a typical memory profiler will
provide memory usage per method name
•Use CI tools – Use CI tool to detect failures before sending to production. For example, use ESLint to detect reference
errors and undefined variables. Use –trace-sync-io to identify code that uses synchronous APIs (instead of the async
version)
•Log wisely – Include in each log statement contextual information, hopefully in JSON format so log aggregators tools such
as Elastic can search upon those properties (see separate bullet – ‘Increase visibility using smart logs’). Also, include
transaction-id that identifies each request and allows to correlate lines that describe the same transaction (see separate
bullet – ‘Include Transaction-ID’)
•Error management – Error handling is the Achilles’ heel of Node.js production sites – many Node processes are crashing
because of minor errors while others hang on alive in a faulty state instead of crashing. Setting your error handling strategy
is absolutely critical, read here my error handling best practices
5.10. Measure and guard the memory usage

TL;DR: Node.js has controversial relationships with memory: the v8


engine has soft limits on memory usage (1.4GB) and there are known
paths to leaks memory in Node’s code – thus watching Node’s
process memory is a must. In small apps, you may gauge memory
periodically using shell commands but in medium-large app consider
baking your memory watch into a robust monitoring system

Otherwise: Your process memory might leak a hundred megabytes a


day like how it happened at Walmart
5.10. Measure and guard the memory usage

One Paragraph Explainer


In a perfect world, a web developer shouldn’t deal with memory leaks. In reality,
memory issues are a known Node’s gotcha one must be aware of. Above all,
memory usage must be monitored constantly. In the development and small
production sites, you may gauge manually using Linux commands or NPM tools
and libraries like node-inspector and memwatch. The main drawback of this
manual activities is that they require a human being actively monitoring – for
serious production sites, it’s absolutely vital to use robust monitoring tools e.g.
(AWS CloudWatch, DataDog or any similar proactive system) that alerts when a
leak happens. There are also few development guidelines to prevent leaks: avoid
storing data on the global level, use streams for data with dynamic size, limit
variables scope using let and const.
“What Other Bloggers Say”

... ”As we already learned, in Node.js JavaScript is compiled to native code by V8.
The resulting native data structures don’t have much to do with their original
representation and are solely managed by V8. This means that we cannot actively
allocate or deallocate memory in JavaScript. V8 uses a well-known mechanism
called garbage collection to address this problem.”

From the blog 


Dyntrace
“What Other Bloggers Say”

... “Although this example leads to obvious results the process is


always the same: Create heap dumps with some time and a fair
amount of memory allocation in between Compare a few dumps to find
out what’s growing”

From the blog 


Dyntrace
“What Other Bloggers Say”

... “fault, Node.js will try to use about 1.5GBs of memory, which has to be capped when running on
systems with less memory. This is the expected behavior as garbage collection is a very costly
operation. The solution for it was adding an extra parameter to the Node.js process: node –
max_old_space_size=400 server.js –production ” “Why is garbage collection expensive? The V8
JavaScript engine employs a stop-the-world garbage collector mechanism. In practice, it means
that the program stops execution while garbage collection is in progress.”

From the blog 


Dyntrace
5.11. Get your frontend assets out of Node
TL;DR: Serve frontend content using dedicated middleware
(nginx, S3, CDN) because Node performance really gets hurt
when dealing with many static files due to its single threaded
model

Otherwise: Your single Node thread will be busy streaming


hundreds of html/images/angular/react files instead of
allocating all its resources for the task it was born for –
serving dynamic content
5.11. Get your frontend assets out of Node

One Paragraph Explainer


In a classic web app the backend serves the frontend/graphics to the browser, a very common approach in the
Node’s world is to use Express static middleware for streamlining static files to the client. BUT – Node is not a
typical webapp as it utilizes a single thread that is not optimized to serve many files at once. Instead, consider
using a reverse proxy (e.g. nginx, HAProxy), cloud storage or CDN (e.g. AWS S3, Azure Blob Storage, etc) that
utilizes many optimizations for this task and gain much better throughput. For example, specialized middleware
like nginx embodies direct hooks between the file system and the network card and uses a multi-threaded
approach to minimize intervention among multiple requests.
Your optimal solution might wear one of the following forms:
1.Using a reverse proxy – your static files will be located right next to your Node application, only requests to
the static files folder will be served by a proxy that sits in front of your Node app such as nginx. Using this
approach, your Node app is responsible deploying the static files but not to serve them. Your frontend’s
colleague will love this approach as it prevents cross-origin-requests from the frontend.
2.Cloud storage – your static files will NOT be part of your Node app content, they will be uploaded to services
like AWS S3, Azure BlobStorage, or other similar services that were born for this mission. Using this approach,
your Node app is not responsible deploying the static files neither to serve them, hence a complete decoupling
is drawn between Node and the Frontend which is anyway handled by different teams.
Configuration example: typical nginx configuration for serving
static files

# configure gzip compression


gzip on;
keepalive 64;

# defining web server


server {
listen 80;
listen 443 ssl;

# handle static content


location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|
favicon.ico) {
root /usr/local/silly_face_society/node/public;
access_log off;
expires max;
}
5.11. Get your frontend assets out of Node

“…In development, you can use res.sendFile() to serve static files. But don’t do
this in production, because this function has to read from the file system for
every file request, so it will encounter significant latency and affect the overall
performance of the app. Note that res.sendFile() is not implemented with the
sendfile system call, which would make it far more efficient. Instead, use serve-
static middleware (or something equivalent), that is optimized for serving files
for Express apps. An even better option is to use a reverse proxy to serve static
files; see Use a reverse proxy for more information…”

From the blog 

StrongLoop
5.13. Use tools that automatically detect vulnerabilities

TL;DR: Even the most reputable dependencies such as Express have


known vulnerabilities (from time to time) that can put a system at
risk. This can get easily tamed using community and commercial
tools that constantly check for vulnerabilities and warn (locally or at
GitHub), some can even patch them immediately

Otherwise: Otherwise: Keeping your code clean from vulnerabilities


without dedicated tools will require to constantly follow online
publications about new threats. Quite tedious
5.13. Use tools that automatically detect vulnerabilities

One Paragraph Explainer


Modern Node applications have tens and sometimes hundreds of
dependencies. If any of the dependencies you use has a known
security vulnerability your app is vulnerable as well. The following
tools automatically check for known security vulnerabilities in
your dependencies:
•nsp - Node Security Project
•snyk - Continuously find & fix vulnerabilities in your dependencies
“What Other Bloggers Say”

“...Using to manage your application’s dependencies is powerful and convenient. But the
packages that you use may contain critical security vulnerabilities that could also affect your
application. The security of your app is only as strong as the “weakest link” in your dependencies.
Fortunately, there are two helpful tools you can use to ensure the third-party packages you use:
and requireSafe. These two tools do largely the same thing, so using both might be overkill, but
“better safe than sorry” are words to live by when it comes to security...”

From the blog

StrongLoop 
 5.14. Assign ‘TransactionId’ to each log statement

TL;DR: Assign the same identifier, transaction-id: {some value}, to each


log entry within a single request. Then when inspecting errors in logs,
easily conclude what happened before and after. Unfortunately, this is
not easy to achieve in Node due to its async nature, see code
examples inside

Otherwise: Looking at a production error log without the context –


what happened before – makes it much harder and slower to reason
about the issue
 5.14. Assign ‘TransactionId’ to each log statement

One Paragraph Explainer

A typical log is a warehouse of entries from all components and requests. Upon detection of some suspicious
line or error, it becomes hairy to match other lines that belong to the same specific flow (e.g. the user “John”
tried to buy something). This becomes even more critical and challenging in a microservice environment when
a request/transaction might span across multiple computers. Address this by assigning a unique transaction
identifier value to all the entries from the same request so when detecting one line one can copy the id and
search for every line that has similar transaction Id. However, achieving this In Node is not straightforward as
a single thread is used to serve all requests –consider using a library that that can group data on the request
level – see code example on the next slide. When calling other microservice, pass the transaction Id using an
HTTP header like “x-transaction-id” to keep the same context.
Code example: typical Express configuration

// when receiving a new request, start a new isolated context and set a transaction Id. The following example is using the NPM library
continuation-local-storage to isolate requests

const { createNamespace } = require('continuation-local-storage');


var session = createNamespace('my session');

router.get('/:id', (req, res, next) => {


session.set('transactionId', 'some unique GUID');
someService.getById(req.params.id);
logger.info('Starting now to get something by Id');
});

// Now any other service or components can have access to the contextual, per-request, data
class someService {
getById(id) {
logger.info(“Starting to get something by Id”);
// other logic comes here
}
}

// The logger can now append the transaction-id to each entry so that entries from the same request will have the same value
class logger {
info (message)
{console.log(`${message} ${session.get('transactionId')}`);}
}
5.15. Set NODE_ENV=production

TL;DR: Set the environment variable NODE_ENV to ‘production’ or


‘development’ to flag whether production optimizations should get
activated – many NPM packages determining the current
environment and optimize their code for production

Otherwise: Omitting this simple property might greatly degrade


performance. For example, when using Express for server-side
rendering omitting NODE_ENV makes the slower by a factor of three!
5.15. Set NODE_ENV=production

One Paragraph Explainer


Process environment variables is a set of key-value pairs made available to any
running program, usually for configuration purposes. Though any variables can be
used, Node encourages the convention of using a variable called NODE_ENV to flag
whether we’re in production right now. This determination allows components to
provide better diagnostics during development, for example by disabling caching or
emitting verbose log statements. Any modern deployment tool – Chef, Puppet,
CloudFormation, others – support setting environment variables during
deployment
Code example: Setting and reading the NODE_ENV environment
variable

// Setting environment variables in bash before starting the node


process
$ NODE_ENV=development
$ node

// Reading the environment variable using code


if (process.env.NODE_ENV === “production”)
useCaching = true;
“What Other Bloggers Say”

“...In Node.js there is a convention to use a variable called NODE_ENV to set the
current mode. We see that it, in fact, reads NODE_ENV and defaults to
‘development’ if it isn’t set. We clearly see that by setting NODE_ENV to production
the number of requests Node.js can handle jumps by around two-thirds while the
CPU usage even drops slightly. Let me emphasize this: Setting NODE_ENV to
production makes your application 3 times faster!”

From the blog 

dynatrace
5.16. Design automated, atomic and zero-downtime
deployments

TL;DR: Researches show that teams who perform many deployments – lowers


the probability of severe production issues. Fast and automated deployments that
don’t require risky manual steps and service downtime significantly improves the
deployment process. You should probably achieve that using Docker combined
with CI tools as they became the industry standard for streamlined deployment

Otherwise: Long deployments -> production down time & human-related error ->
team unconfident and in making deployment -> less deployments and features
5.17. Use an LTS release of Node.js

TL;DR: Ensure you are using an LTS version of Node.js to receive


critical bug fixes, security updates and performance improvements

Otherwise: Newly discovered bugs or vulnerabilities could be used to


exploit an application running in production, and your application may
become unsupported by various modules and harder to maintain
5.17. Use an LTS release of Node.js

One Paragraph Explainer


Ensure you are using an LTS(Long Term Support) version of Node.js in
production to receive critical bug fixes, security updates and performance
improvements.
LTS versions of Node.js are supported for at least 18 months and are indicated
by even version numbers (e.g. 4, 6, 8). They're best for production since the LTS
release line is focussed on stability and security, whereas the 'Current' release
line has a shorter lifespan and more frequent updates to the code. Changes to
LTS versions are limited to bug fixes for stability, security updates, possible
npm updates, documentation updates and certain performance improvements
that can be demonstrated to not break existing applications.
Appendix

Part 6 – Security Best Practices


6.1. Embrace linter security rules

TL;DR: Make use of security linter plugins such as eslint


-plugin-security to enforce a policy for secure code (e.g. no use of
eval, require with variables, etc).

Otherwise: Developers in the project may not follow consistent


code security practices, leading to vulnerabilities being introduced,
or sensitive secrets committed into remote repositories.
6.1. Embrace linter security rules

One Paragraph Explainer


Security plugins for ESLint such as eslint-plugin-security offer code
security checks based on a number of known vulnerabilities, such as
unsafe regex, unsafe use of eval(), and non literal filenames being used
when accessing the file system within an application. The use of git
hooks such as pre-git allows to further enforce any rules on source
control before they get distributed to remotes, one of which can be to
check that no secrets were added to source control.
“What Other Bloggers Say”

“Linting doesn’t have to be just a tool to enforce pedantic rules about whitespace,
semicolons or eval statements. ESLint provides a powerful framework for
eliminating a wide variety of potentially dangerous patterns in your code (regular
expressions, input validation, and so on). I think it provides a powerful new tool
that’s worthy of consideration by security-conscious JavaScript developers.”

From the blog 

Adam Baldwin
6.2. Limit concurrent requests using a balancer or a
middleware

TL;DR: Implement rate limiting using an external service such


as NGINX, or if that is not possible use a rate limiting
middleware within the application

Otherwise: An application could be subject to an attack


resulting in a denial of service where real users receive
degraded service, or an unavailable application.
6.2. Limit concurrent requests using a balancer or a
middleware

One Paragraph Explainer

Rate limiting should be implemented in your application to protect a Node.js


application from being overwhelmed by too many requests at the same time.
Rate limiting is a task best performed with a service designed for this task,
such as nginx, however it is also possible with application middleware such
as express-rate-limiter.
Code example: Express rate limiting middleware for certain
routes

var RateLimit = require('express-rate-limit');


// important if behind a proxy to ensure client IP is passed to req.ip
app.enable('trust proxy');

var apiLimiter = new RateLimit({


windowMs: 15*60*1000, // 15 minutes
max: 100,
delayMs: 0 // disabled
});

// only apply to requests that begin with /user/


app.use('/user/', apiLimiter);
“What Other Bloggers Say”

“Rate limiting can be used for security purposes, for example to slow down
brute‑force password‑guessing attacks. It can help protect against DDoS attacks
by limiting the incoming request rate to a value typical for real users, and (with
logging) identify the targeted URLs. More generally, it is used to protect upstream
application servers from being overwhelmed by too many user requests at the
same time.”

From the

NGINX blog
6.3 Extract secrets from config files or use NPM package that
encrypts them

TL;DR: Never store plain-text secrets in configuration files or source code.


Instead, make use of secrets management systems like Vault, Docker Secrets,
or inject secrets as environment variables accessible to an application. As a last
result, storing secrets in source control must be encrypted, and managed
(rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to
check for accidental commit of secrets.

Otherwise: Source control for even private repositories, can mistakenly be made


public, at which point all secret has been exposed outside. Access to source
control for an external party will inadvertently provide access to related systems
(database, apis, etc).
6.4. Prevent SQL/noSQL injection with ORM/ODM or any other
DAL packages
TL;DR: To prevent SQL/noSQL injection attacks always make use of
an ORM/ODM or a database library that escapes or supports named
or indexed parameter binding, and takes care of validating user input
for expected types. Never use JS template strings or string
concatenation to inject values into queries.

Otherwise: Un-validated user input could lead to operator injection


when working with MongoDB for noSQL, and unescaped use of
proper ORM/ODM will allow easy SQL injection attacks.
6.5. Collection of common generic security best practices (15
items)

TL;DR: There are many built-in and external ways to secure


your Node.js applications starting from simple steps like
running your server with ssl/tls enabled to more advanced
features.
Common Node.js security best practices

The common security guidelines section contains best


practices that are standardized in many frameworks and
conventions, running an application with ssl/tls for example
should be a common guideline and convention followed in
every setup to achieve great security benefits.
Use SSL/TLS to encrypt the client-server connection

TL;DR: In the times of free SSL/TLS certificates and easy configuration of


those, you do no longer have to weigh advantages and disadvantages of using
a secure server because the advantages such as security, support of modern
technology and trust clearly outweigh the disadvantages like minimal overhead
compared to pure http.

Otherwise: Attackers could perform man-in-the-middle attacks, spy on your


users' behaviour and perform even more malicious actions when the
connection is unencrypted
Using HTTPS to encrypt the client-server connection

One Paragraph Explainer

Using services like the Let'sEncrypt certificate authority providing free


ssl/tls certificates, you can easily obtain a certificate to secure your
application. Node.js frameworks like Express (based on the core https
module) support ssl/tls based servers easily, so the configuration can
be done in a few lines of additional code.

You can also configure ssl/tls on your reverse proxy pointing to your
application for example using nginx or HAProxy.
Code Example – Enabling SSL/TLS using the Express
framework

const express = require('express');


const https = require('https');
const app = express();
const options = {
// The path should be changed accordingly to your setup
cert: fs.readFileSync('./sslcert/fullchain.pem'),
key: fs.readFileSync('./sslcert/privkey.pem')
};
https.createServer(options, app).listen(443);
Comparing secret values and hashes securely

TL;DR: When comparing secret values or hashes like HMAC digests, you should
use the crypto.timingSafeEqual(a, b) function Node provides out of the box since
Node.js v6.6.0. This method compares two given objects and keeps comparing
even if data does not match. The default equality comparison methods would
simply return after a character mismatch, allowing timing attacks based on the
operation length.

Otherwise: Using default equality comparison operators you might expose critical
information based on the time taken to compare two objects
Generating random strings using Node.js

TL;DR: Using a custom-built function generating pseudo-random strings for


tokens and other security-sensitive use cases might actually not be as random as
you think, rendering your application vulnerable to cryptographic attacks. When
you have to generate secure random strings, use the crypto.RandomBytes(size,
[callback]) function using available entropy provided by the system.

Otherwise: When generating pseudo-random strings without cryptographically


secure methods, attackers might predict and reproduce the generated results,
rendering your application insecure
6.6. Adjust the response HTTP headers for enhanced security

TL;DR: Your application should be using secure headers to prevent


attackers from using common attacks like cross-site scripting (XSS),
clickjacking and other malicious attacks. These can be configured
easily using modules like helmet.

Otherwise: Attackers could perform attacks on your application's


users, leading to insecurity
6.6. Adjust the response HTTP headers for enhanced security

One Paragraph Explainer

There are security-related headers used to secure your application


further. The most important headers are listed below. You can also
visit the sites linked at the bottom of this page to get more
information on this topic. You can easily set these headers using the 
Helmet module for express (Helmet for koa).
6.7. Constantly and automatically inspect for vulnerable
dependencies

TL;DR: With the npm ecosystem it is common to have many


dependencies for a project. Dependencies should always be kept in
check as new vulnerabilities are found. Use tools like nsp or snyk to
track, monitor and patch vulnerable dependencies. Integrate these
tools with your CI setup so you catch a vulnerable dependency before
it makes it to production.

Otherwise: An attacker could detect your web framework and attack


with all it's known vulnerabilities.
6.7. Constantly and automatically inspect for vulnerable
dependencies
One Paragraph Explainer
The majority of Node.js applications rely heavily on a large number of third
party modules from NPM or Yarn package managers due to ease and speed of
development. However, the downside to this benefit is the security risks of
including unknown vulnerabilities into your application, which is a risk
recognised by it's place in the OWASP top critical web application security
risks list.
There are a number of tools available to help identify third party packages in
Node.js applications which have been identified as vulnerable by the
community to mitigate the risk of introducing them into your project. These
can be used periodically from CLI tools or included as part of your
application's build process.
6.8. Avoid using the Node.js Crypto library for passwords, use
Bcrypt

TL;DR: Passwords or secrets (API keys) should be stored using a


secure hash function like bcrypt, that should be a preferred choice over
its JavaScript implementation due to performance and security
reasons.

Otherwise: Passwords or secrets that are persisted without using a


secure hash function are vulnerable to brute forcing and dictionary
attacks that will lead to their disclosure eventually.
6.8. Avoid using the Node.js Crypto library for passwords, use
Bcrypt
One Paragraph Explainer
When storing user passwords, using an adaptive hashing algorithm such as
bcrypt, offered by the bcrypt npm module is recommended as opposed to using
the native Node.js Crypto module. Math.random() should also never be used for
as part of any password or token generation due to it's predictability.

The bcrypt module or similar should be used as opposed to the JavaScript


implementation, as when using bcrypt, a number of 'rounds' can be specified in
order to provide a secure hash. This sets the work factor or the number of
'rounds' the data is processed for, and more hashing rounds leads to more secure
hash (although this at the cost of CPU time). The introduction of hashing rounds
means that the brute force factor is significantly reduced, as password crackers
are slowed down increasing the time required to generate one attempt.
Code Example – using promises to catch
errors

// asynchronously generate a secure password using 10 hashing rounds


bcrypt.hash('myPassword', 10, function(err, hash) {
// Store secure hash in user record
});

// compare a provided password input with saved hash


bcrypt.compare('somePassword', hash, function(err, match) {
if(match) {
// Passwords match
} else {
// Passwords don't match
}
});
“What Other Bloggers Say”

“... it’s not just using the right hashing algorithm. I’ve talked extensively about how
the right tool also includes the necessary ingredient of “time” as part of the
password hashing algorithm and what it means for the attacker who’s trying to
crack passwords through brute-force.”

From the blog by

Max McCarty
 6.9. Use middleware that sanitizes input and output

TL;DR: Here we will write about sanitizing/escaping/encoding both the input and


output. The top frontend libraries handle output encoding well, but nevertheless
you should always make sure to output encode your user generated data in the
correct context, and can make use of libraries such as node-esapi or escape-html.

Otherwise: Failure to encode user generated when outputting it can result in XSS,


Log Injection or other vulnerabilities. Input validation should always be performed
to confirm one is working with expected types and data properties (length, range,
etc).
6.10. Validate the incoming JSON schemas

TL;DR: Validate the incoming requests` body payload and ensure it qualifies


the expectations, fail fast if it doesn't. To avoid tedious validation coding
within each route you may use lightweight JSON-based validation schemas
such as jsonschema or JOI

Otherwise: Your generosity and permissive approach greatly increases the


attack surface and encourage the attacker to try out many inputs until it finds
some combination that crashes the application
6.10. Validate the incoming JSON schemas

One Paragraph Explainer


Validation is about being very explicit on what payload our app is willing to accept and failing fast
should the input deviates from the expectations. This minimizes an attackers surface who can no
longer try out payloads with a different structure, values and length. Practically it prevents attacks
like DDOS (code is unlikely to fail when the input is well defined) and Insecure Deserialization
(JSON contain no surprises ). Though validation can be coded or rely upon classes and types (TS,
ES6 classes) the community seems to increasingly like JSON-based schemas as these allow
declaring complex rules without coding and share the expectations with the frontend. JSON-
schema is an emerging standard that is supported by many NPM libraries and tools (e.g. 
jsonschema, POSTMAN), JOI is also highly popular with sweet syntax. Typically JSON syntax can't
cover all validation scenario and custom code or pre-baked validation frameworks like validator.js
 come in handy. Regardless of the chosen syntax, ensure to run the validation as early as possible -
For example, by using Express middleware that validates the request body before the request is
passed to the route handler
Example - JSON-Schema validation rules

{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": ["id", "name", "price"]
}
Example - Validating an entity using JSON-Schema

const JSONValidator = require("jsonschema").Validator;

class Product {

validate() {
var v = new JSONValidator();

return v.validate(this, schema);


}

static get schema() {


//define JSON-Schema, see example above
}
}
Example - Usage of middleware validator

// The validator is a generic middleware that gets the entity it should validate and takes care to return
// HTTP status 400 (Bad Request) should the body payload validation fail
router.post("/" , **validator(Product.validate)**, async (req, res, next) => {
// route handling code goes here
});
“What Other Bloggers Say”

“Validating user input is one of the most important things to do when it comes to
the security of your application. Failing to do it correctly can open up your
application and users to a wide range of attacks, including command injection,
SQL injection or stored cross-site scripting.”

From the blog

Nemeth Gergley
6.11. Support blacklisting JWT tokens

TL;DR: When using JWT(for example, with Passport.js), a


custom implementation for blacklisting untrusted or invalid
tokens is required to revoke the token's access, due to the
completely stateless approach of JWT.

Otherwise: Expired, or misplaced tokens could be used


maliciously by a third party to access an application
impersonating the owner of the token.
6.13. Run Node.js as non-root user

TL;DR: There are common scenario where nodejs runs as a root user with
unlimited permissions. For example this is the default behaviour in Docker
containers. It's recommended to create a non-root user and always run the
process on this user behalf by invoking the container with the flag "-u username“

Otherwise: An attacker who manages to run a script on the server gets unlimited
power over the local machine (e.g. change iptable and re-route traffic to his
server)
6.13. Run Node.js as non-root user

One Paragraph Explainer


According to the 'Principle of least privilege' a user/process must be able to
access only the necessary information and resources. Granting root access to
an attacker opens a whole new world of malicious ideas like routing traffic to
other servers. In practice, most Node.js apps don't need root access and don't
run with such privileges. However, there are two common scenarios that might
push to root usage: (1) to gain access to privilege port (e.g. port 80) Node.js
must run as root (2) Docker containers by default run as root(!). It's
recommended for Node.js webservers to listen on non-privilege ports and rely on
a reverse-proxy like nginx to redirect incoming traffic from port 80 to your
Node.js application. When building a Docker image, highly secured apps should
run the container with an alternate non-root user. Most Docker clusters (e.g.
Swarm, Kubernetes) allow setting the security context declaratively
Code example - Building a Docker image as non-root

FROM node:latest
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
“What Other Bloggers Say”

“By default, Docker runs container as root which inside of the container can pose
as a security issue. You would want to run the container as an unprivileged user
wherever possible. The node images provide the node user for such purpose. The
Docker Image can then be run with the node user in the following way:”

From the Repository docker-node by 

eyalzek
6.14. Limit payload size using a reverse-proxy or a middleware

TL;DR: DOS attacks of many varieties can be mitigated by limiting


the body size of incoming requests to your web application.

Otherwise: your application will have to deal with large requests,


unable to process the other important work it has to accomplish,
leading to performance implications and vulnerability towards DOS
attacks
6.14. Limit payload size using a reverse-proxy or a middleware

One Paragraph Explainer

Parsing request bodies, for example JSON-encoded payloads, is a performance-


heavy operation, especially with larger requests. When handling incoming requests
in your web application, you should limit the size of their respective payloads.
Incoming requests with unlimited body/payload sizes can lead to your application
performing badly or crashing due to a denial-of-service outage or other unwanted
side-effects. Many popular middleware-solutions for parsing request bodies, such
as the already-included body-parser package for express, expose options to limit
the sizes of request payloads, making it easy for developers to implement this
functionality. You can also integrate a request body size limit in your reverse-
proxy/web server software if supported. Below are examples for limiting request
sizes using express and/or nginx.
Example code for express

const express = require('express');

const app = express();

app.use(express.json({ limit: '300kb' })); // body-parser defaults to a body size limit of 100kb

// Request with json body


app.post('/json', (req, res) => {

// Check if request payload content-type matches json, because body-parser does not check for content types
if (!req.is('json')) {
return res.sendStatus(415); // -> Unsupported media type if request doesn't have JSON body
}

res.send('Hooray, it worked!');
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));


Example configuration for nginx

http {
...
# Limit the body size for ALL incoming requests to 1 MB
client_max_body_size 1m;
}

server {
...
# Limit the body size for incoming requests to this specific server block to 1 MB
client_max_body_size 1m;
}

location /upload {
...
# Limit the body size for incoming requests to this route to 1 MB
client_max_body_size 1m;
}
 6.15. Avoid JS eval statements

TL;DR: eval may be used to evaluate javascript code during run-time, but it is
not just a performance concern but also an important security concern due to
malicious javascript code that may be sourced from user input. Another
language feature that should be avoided is new Function constructor.
setTimeout and setInterval should never be passed dynamic javascript code
either.

Otherwise: Malicious javascript code finds a way into a text passed into eval or
other real-time evaluating javascript language functions, it will gain complete
access to javascript permissions on the page, often manifesting as an XSS
attack.
 6.15. Avoid JS eval statements

One Paragraph Explainer


eval(), setTimeout() and setInterval() are global functions often used in Node.js
which accept a string parameter representing a JavaScript expression, statement,
or sequence of statements. The security concern of using these functions are the
possibility that untrusted user input that might find it's way into code execution
which can lead to server compromise, as evaluating user inputted code essentially
allows an attacker to perform any actions that you can. It is suggested to refactor
code to not rely on the use of these functions where user input could be passed to
the function and executed.
Code example

// example of malicious code which an attacker was able to input


userInput = "require('child_process').spawn('rm', ['-rf', '/'])";
// malicious code executed
eval(userInput);
“What Other Bloggers Say”

“The eval() function is perhaps of the most frowned upon JavaScript pieces from a
security perspective. It parses a JavaScript string as text, and executes it as if it
were a JavaScript code. Mixing that with untrusted user input that might find it’s
way to eval() is a recipe for disaster that can end up with server compromise.”

From the Essential Node.js Security book by

Liran Tal
6.16. Prevent malicious RegEx from overloading your single
thread execution

TL;DR: Regular Expressions, while being handy, pose a real threat to


JavaScript applications at large, and the Node.js platform in
particular due to the fact that they require CPU cycles to compute a
pattern test. Use the aforementioned validator.js package to validate
data instead of writing your own, or make use of safe-regex to
detect vulnerable regex patterns.

Otherwise: Poorly written regexes could be susceptible to Regular


Expressions DoS attacks that will block the event loop completely.
For example, the popular moment package was found vulnerable in
Nov 2017.
6.16. Prevent malicious RegEx from overloading your single
thread execution
One Paragraph Explainer

The risk that is inherent with the use of Regular Expressions is the
computational resources that require to parse text and match a given
pattern. For the Node.js platform, where a single-thread event-loop is
dominant, a CPU-bound operation like resolving a regular expression
pattern will render the application unresponsive. Avoid regex when
possible or defer the task to a dedicated library like validator.js, or 
safe-regex to check if the regex pattern is safe.
Some OWASP examples for vulnerable regex patterns:
•(a|aa)+
•([a-zA-Z]+)*
Code Example – using promises to catch
errors

var saferegex = require('safe-regex');


var emailRegex = /^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]
{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/;

// should output false because the emailRegex is vulnerable to redos attacks


console.log(saferegex(emailRegex));

// instead of the regex pattern, use validator:


var validator = require('validator');
console.log(validator.isEmail('liran.tal@gmail.com'));
“What Other Bloggers Say”

“Often, programmers will use RegEx to validate that an input received from a user
conforms to an expected condition. A vulnerable Regular Expression is known as
one which applies repetition to a repeating capturing group, and where the string
to match is composed of a suffix of a valid matching pattern plus characters that
aren't matching the capturing group.”

From the Essential Node.js Security book by

Liran Tal
6.17. Avoid module loading require(someVariable) using a
variable
TL;DR: Avoid requiring/importing another file with a path that was given as
parameter due to the concern that it could have originated from user input.
This rule can be extended for accessing files in general (i.e. fs.readFile()) or
other sensitive resource access with dynamic variables originating from user
input.

Otherwise: Malicious user input could find its way to a parameter that is used
to require tampered files, for example a previously uploaded file on the
filesystem, or access already existing system files.
Code example

// insecure, as helperPath variable may have been modified by user input


const uploadHelpers = require(helperPath);

// secure
const uploadHelpers = require('./helpers/upload');
6.18. Run unsafe code in a sandbox

TL;DR: Here we will write about the need to run


untrusted code, for example third-party components.
In that case, we'd like to isolate our code from
weakness of the 3rd party code. We can achieve that
by running our code in a sandbox
6.19. Take extra care when working with child processes

TL;DR: Avoid using child_process.exec when possible or


validate and sanitize input to mitigate shell injection attacks.
Prefer using child_process.execFile which by definition will
only execute a single command with a set of attributes and
will not allow shell parameter expansion.

Otherwise: Naive use of child_process.exec could result in


remote command execution or shell injection attacks due to
malicious user input passed to an unsanitized system
command.
6.20. Hide error details from client (e.g. default Express
behaviour)

TL;DR: Use the default Express error handler, or ensure that custom


error handlers avoid exposing error details to the client when running
an application outside of development.

Otherwise: Sensitive application details such as server filepaths,


third party modules in use, and other internal workings of the
application which could be exploited by an attacker from
information found in a stack trace.
6.20. Hide error details from client (e.g. default Express
behaviour)
One Paragraph Explainer

Exposing application error details to the client in production should be avoided due to risk
of exposing sensitive application details such as server filepaths, third party modules in
use, and other internal workings of the application which could be exploited by an attacker.
Express comes with a built-in error handler, which takes care of any errors that might be
encountered in the app. This default error-handling middleware function is added at the
end of the middleware function stack. If you pass an error to next() and you do not handle
it in a custom error handler, it will be handled by the built-in Express error handler; the error
will be written to the client with the stack trace. This behaviour will be true when
NODE_ENV is set to development, however when NODE_ENV is set to production, the stack
trace is not written, only the HTTP response code.
Code example

// production error handler


// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
6.21. Modify session middleware settings
TL;DR: Using the default settings for session middleware can be
expose your app to module and framework specific hijacking
attacks in a similar way to the X-Powered-By header.

Otherwise: Cookies could be sent over insecure connections, and


an attacker can use session identification to identify the
underlying framework of the web application, as well as module-
specific vulnerabilities.
6.21. Modify session middleware settings

One Paragraph Explainer


Many popular session middlewares do not apply best practice/secure cookie settings out of the box.
Tweaking these settings from the defaults offers more protection for both the user and the application, by
reducing the threat of attacks such as session hijacking and session identification.

The most common setting left to default is the session name - in express-session this is connect.sid. An
attacker can use this information to identify the underlying framework of the web application as well as
module specific vulnerabilities. Changing this value to something other than the default will make it harder
to determine what session mechanism is being used.

Also in express-session, the option cookie.secure is set to false as the default value. Changing this to true
will restrict transport of the cookie to https only which provides safety from man in the middle type attacks
Code example: Setting secure cookie settings

// using the express session middleware


app.use(session({
secret: 'youruniquesecret', // secret string used in the signing of the session ID that is stored in the cookie
name: 'youruniquename', // set a unique name to remove the default connect.sid
cookie: {
httpOnly: true, // minimize risk of XSS attacks by restricting the client from reading the cookie
secure: true, // only send cookie over https
maxAge: 60000*60*24 // set cookie expiry length in ms
}
}));
“What Other Bloggers Say”

“...Express has default cookie settings that aren’t highly secure. They can be
manually tightened to enhance security - for both an application and its user.*”
From the NodeSource blog:

From the

NodeSource blog
6.21. Modify session middleware
settings

One Paragraph Explainer

Avoid requiring/importing another file with a path that was


given as parameter due to the concern that it could have
originated from user input. This rule can be extended for
accessing files in general (i.e. fs.readFile()) or other sensitive
resource access with dynamic variables originating from user
input.
6.21. Modify session middleware settings

One Paragraph Explainer

Avoid requiring/importing another file with a path that was


given as parameter due to the concern that it could have
originated from user input. This rule can be extended for
accessing files in general (i.e. fs.readFile()) or other sensitive
resource access with dynamic variables originating from user
input.
End

You might also like