Notes On Node.JS

Table of Contents

References

Synopsis

node -v          # v11.2.0
nvm --version    # 0.33.11 
nvm ls           # List installed versions
                 # system, node, iojs
npm --version    # 6.4.1

Node.js Source

  • Node uses Google’s V8 JavaScript engine for language implementation. New language features in Node depend on them being implemented first in V8.

  • https://node.green/ lists ES6 (2015), etc compatibility.

  • To find Chrome V8 engine version:

    node -v 
       v11.2.0                    # Node version
    
    node -p process.versions.v8
       7.0.276.38-node.11         # Chrome V8 engine version
    
  • Grunt and Gulp are 2 most popular task runners.

  • .travis.yml is a testing config file using Travis CI. .jshintrc is for JS linter. jslint and jshint are popular linters. confg.js is a config file. NODE_ENV=my_env_name node index.js

  • airbnb/javascript gives javascript style guide.

Libraries

Note: In Client-side rendering, your browser downloads a minimal HTML page. It renders the JavaScript and fills the content into it. Server-side rendering, on the other hand, renders the React components on the server. The output is HTML content.

React JS

  • React JS is a front-end development framework like Angular JS.
  • Created by facebook.
  • Both client side rendering and Server side rendering is possible.
  • Supports JSX, JS ES2015 and later. Angular supports Typescript. Javascript now looks much better after ES6, so TypeScript not needed.
  • Easier to learn (compared to Angular)
  • React JS does One way data binding (against 2 way binding of Angular). i.e. updating data reflects automatically in UI. But changes in UI does not auto update the data -- You can still setup input event handler and change the data.

Kracken Suite From Paypal

  • Kraken is a secure and scalable layer that extends express by providing structure and convention.
  • Lusca application security
  • Adaro - Dust templating

Electrode.io from Walmart

Electrode is a platform for building universal React/Node.js applications with standardized structure, best practices, and modern technologies baked in.

History of Node JS

  • Ryan Dahl creates Node JS using chrome engine in 2009
  • MongoDB in 2010 fits better with JavaScript with Json (JavaScript Object Notation) Support.
  • In 2010, twitter like Social media API supports Rest Json API with better support with MongoDB. No need to translate nested objects JSON for rdbms.
  • NPM support appeared by Jan 2010
  • Angular JS - A frontend library released by Google by Oct 2010.
  • Express JS released by 2010 Nov by TJ, Serverside MVC framework for Node.JS
  • Mongoose released by 2011, a MongoDB driver.
  • MEAN Stack established - MongoDB, Express, Angular, Node or NPM.
  • Joyent hire Ryan Dahl and provided corporate backup for Node JS by 2011
  • Node JS foundation has taken over Node later.
  • Many Node JS developers tend to use third party NPM packages instead of sticking to many core native modules (like fs) Node JS APIs - This must change.

Installation

Synopsis :

npm install gulp  --save  # It will update ./package.json 
                          # with dependencies as "gulp"


The AWS Amplify CLI is a toolchain for mobile and web app development.
https://github.com/aws-amplify/amplify-cli

package.json dependencies can specify version like 1.x which means
the latest minor version of major version 1.

Promises and Async/Await support

Summary

  • Legacy style is to call f(p1, p2, callback_func)

  • Better is to use promise and function chaining so that you call, f(p1, p2).then(cb) because indentation is better and you can call then() later point of time.

  • Even better is to use await: let result = await f(p1, p2);

  • Promise chaining can be useful to build async dependent calls.

  • Calling promise.then(cb) multiple times amount to specifying multiple handlers for the same promise. They execute independently once the initial promise resolves.

  • You should never throw() from inside of promise handlers. The promise.catch(error) is different from try { } catch(err) synax -- the .catch() is just a plain function call which captures the return value of reject() function.

  • To make async function call look like sync, you can do :

    export function promiseValue(p)
    {
      return (async (pr) => await pr.then( (v) => v ) )(p)
    }
    

Typical Callback style

app.get('/', my_callback_func)

Function Chaining

Example: Before async/await :

function fun1(req, res){
  return request.get('http://localhost:3000')
   .catch((err) =>{
     console.log('found error');
   }
}).then((res) =>{
   console.log('get request returned.');
});

The code basically says that GET localhost:3000, catch the error if there is any; if there is no error then implement the following statement: console.log(‘get request returned.’);

The above code demos a function implemented with function chaining instead of callbacks. To be able to use function chaining, the function request.get() must return "a Promise" type which supports .then() method.

Async/Await support

With Node v8, the async/await feature was officially rolled out by the Node to deal with Promises and function chaining. The functions need not to be chained one after another, simply await the function that returns the Promise. But the function async needs to be declared before awaiting a function returning a Promise. The code now looks like below.

Example: After async/await :

async function fun1(req, res){
  let response = await request.get('http://localhost:3000');
    if (response.err) { console.log('error');}
    else { console.log('fetched response');
}

Explanation:

The code above basically asks the javascript engine running the code to wait for the request.get() function to complete before moving on to the next line to execute it. The request.get() function returns a Promise for which user will await

Promise Chaining

The return values of .then() functions are "promisified" so that you can make use of promise() chaining. :

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000);

}).then(function(result) {

  alert(result); // 1

  return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) { // (**)

  alert(result); // 2
  return 4;             // Value 4 is wrapped as resolved Promise().

}).then(function(result) {

  alert(result); // 4

});

Promisify XMLHttpRequest

function httpGet(url) {
    return new Promise(
        function (resolve, reject) {
            const request = new XMLHttpRequest();
            request.onload = function () {
                if (this.status === 200) {
                    // Success
                    resolve(this.response);
                } else {
                    // Something went wrong (404 etc.)
                    reject(new Error(this.statusText));
                }
            };
            request.onerror = function () {
                reject(new Error(
                    'XMLHttpRequest Error: '+this.statusText));
            };
            request.open('GET', url);
            request.send();
        });
}
This is how you use httpGet():

httpGet('http://example.com/file.txt')
.then(
    function (value) {
        console.log('Contents: ' + value);
    },
    function (reason) {
        console.error('Something went wrong', reason);
    });

Built-in Promisify

The promisify function is built-in into node 8. No need to use bluebird. :

const fs = require('fs')
const { promisify } = require('util')    // util is built-in for Node 8
const readFile = promisify(fs.readFile)

Check if object is a promise

function isPromise(value) {
  return Boolean(value && typeof value.then === 'function');
}

See Also:

ES6 Features

Template Literal

// ES5
var name = 'Mukul Latiyan';
console.log('My name is '+ name);

// ES6
const name1 = 'Mukul Latiyan';
console.log(`My name is ${name1}`);

var vs let vs const

  • var implies function scope
  • let implies block scope
  • const implies constant binding of variable (not for readonly object) i.e. A const object content is still mutable but var binding is not.
  • If you ommit var, then the variable becomes global.

Object Destructing

Object unpacking is supported.

Object unpacking :

// ES6
const college = {
    name : 'DTU',
    established : '1941',
    isPrivate : false
};

let{name,established,isPrivate} = college;

Array unpacking :

// ES6
const arr = ['lionel','messi','barcelona'];

let[value1,value2,value3] = arr;

Default Parameters

// ES5
function fun(a,b){
    b = (typeof b!=='undefined')?b:1;
    return a + b;
}

console.log(fun(2,1)); // 3
console.log(fun(2)); // 3

// ES6 
function fun(a,b=1){ 
    return a + b; 
} 

classes support

// classes in ES6
class Vehicle{
    constructor(name,engine){
        this.name = name;
        this.engine = engine;
    }
}

const bike1 = new Vehicle('Ninja ZX-10R','998cc');

============= extends =====================

class formatDate extends Date {
  constructor(dateStr) {
    super(dateStr);
  }

  getFormattedDate() {
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

    return `${this.getDate()}-${months[this.getMonth()]}-${this.getFullYear()}`;
  }
}

console.log(new formatDate('August 19, 1975 23:15:30').getFormattedDate());
// expected output: "19-Aug-1975"

================  static method ====================

class ClassWithStaticMethod {
  static staticMethod() {
    return 'static method has been called.';
  }
}

console.log(ClassWithStaticMethod.staticMethod());
// expected output: "static method has been called."

import and export

This is a default import:

// B.js
import A from './A'
It only works if A has the default export:

// A.js
export default 42
In this case it doesn’t matter what name you assign to it when importing:

// B.js
import A from './A'
import MyA from './A'
import Something from './A'
Because it will always resolve to whatever is the default export of A.

This is a named import called A:

import { A } from './A'
It only works if A contains a named export called A:
export const A = 42

// B.js
import { A } from './A'
import { myA } from './A' // Doesn't work!
import { Something } from './A' // Doesn't work!
To make these work, you would add a corresponding named export to A:

// A.js
export const A = 42
export const myA = 43
export const Something = 44

A module can only have one default export, but as many named exports as
you'd like (zero, one, two, or many). You can import them all together:

// B.js
import A, { myA, Something } from './A'

Here, we import the default export as A, and named exports called myA and
Something, respectively.

// A.js
export default 42
export const myA = 43
export const Something = 44
We can also assign them all different names when importing:

// B.js
import X, { myA as myX, Something as XSomething } from './A'

varargs aka rest parameters

// ES6 rest parameter
// function f(a, b, ...theArgs) {  }
function fun(...input){
    let sum = 0;
    for(let i of input){
        sum+=i;
    }
    return sum;
}

console.log(fun(1,2,3)); // 6
console.log(fun(1,2,3,4,5)); // 15

function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}

console.log(sum(1, 2, 3));
// expected output: 6

Spread operator aka Args unpacking

// Spread operator
let arr = [1,2,3,-1];

console.log(Math.min(...arr)); // -1

Convert arguments object to Array

function sortArguments() {
  // arguments is object. But arguments[0] is first element.
  var args = Array.from(arguments);
  var sortedArgs = args.sort();
  return sortedArgs;
}
console.log(sortArguments(5, 3, 7, 1));

Object getter/setter

// ============== setter =================

var language = {
  set current(name) {
    this.log.push(name);
  },
  log: []
}

language.current = 'EN';
language.current = 'FA';

console.log(language.log);
// expected output: Array ["EN", "FA"]

// ============== getter =================

var obj = {
  log: ['a', 'b', 'c'],
  get latest() {
    if (this.log.length == 0) {
      return undefined;
    }
    return this.log[this.log.length - 1];
  }
}

console.log(obj.latest);
// expected output: "c"

Arrow Functions

var materials = [ 'Hydrogen', 'Helium', 'Lithium', 'Beryllium' ];

console.log(materials.map(material => material.length));

Array Iteration

::
const a = ["a", "b", "c"]; for (const val of a) { console.log(val); }

vim configuration for Javascript Editing

You may want to use prettier command installed on your system and also enable prettier plugin for eslint. This enables running prettier automatically as part of lint stage from vim. :

yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
sudo yarn global add prettier

FAQ

Sharing node_modules

Unless you share node_modules, you may be wasting lot of space. The search path goes upwards in directory level. For example, keep node_modules at /home/user, /opt, /disk, etc. My configuration :

/disk/nodebin
    html2body.js
    pre2pretty.js
    ....                                   

/disk/node_modules
    domutils
    lodash
    ....

How do you share constants

const FOO = 'bar';
module.exports = {
  FOO
}

import in bar.js

const {FOO} = require('foo');

Check Javascript Support status

Starting https Server

It is very simple compared to other methods of /etc/ configs :

var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('./keys/key.pem'),
  cert: fs.readFileSync('./keys/cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);   // .... ; res.end("hello world\n");
}).listen(8000);

Debugging

$ node inspect myindex.js

# Insert  debugger; statements with in source code.
debug> cont
debug> repl
> 

For generating lint warnings :

$ node --use_strict myindex.js

For measuring time/performance between calls, use require('perf_hooks')

exports vs require vs module.exports

  • exports is an alias for module.exports
  • require statement returns the module.exports value

::
exports.my_func = my_func module.exports.my_func = my_func # Same as above export my_func # Same as above

  • export

require Vs ES6 import

Conventional Use:

  • Import modules using require, and export using module.exports and exports.foo.
  • Loading is synchronous

ES6 Style:

  • Use import for importing and export statement for exporting.
  • Loading could be asynchronous
  • Babel transpiler converts import and export declaration to CommonJS (require/module.exports)
  • Typically you use Babel to support older browsers or older node versions but you want to use new syntax / feaures.
  • Node was used with Babel earlier, but from version 8.0, there is no need. The import statements are handled by node core itself.

NPM vs YARN

Yarn is superset of NPM. It installs packages in parallel and uses the same NPM registry.

Babel

Babel is a JS transpiler that converts new JS code into old ones.

  • ES6 is same as ES2015.
  • ES5 is mostly supported by all browsers (aka commonJS).
  • One can easily add presets such as es2015, es2016, es2017, so that Babel compiles them to target ES5.

Here is an example — a code written in ES6:

class Test {
  calculatePowers() {
    return [1,2,3].map(n => n ** 2);
  }
}

--------------------------------------------------------------------------
Babel will transpile this code to the following, given the presets es2015:
--------------------------------------------------------------------------

"use strict";
var _createClass = function () { 
    function defineProperties(target, props) { 
      for (var i = 0; i < props.length; i++) { 
         var descriptor = props[i];
         descriptor.enumerable = descriptor.enumerable || false;
         descriptor.configurable = true;
         if ("value" in descriptor) descriptor.writable = true;
         Object.defineProperty(target, descriptor.key, descriptor); 
      } 
    } 
    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor; }; 
}();

function _classCallCheck(instance, Constructor) {
   if (!(instance instanceof Constructor)) { 
      throw new TypeError("Cannot call a class as a function"); 
   } 
}

var Test = function () {
   function Test() { _classCallCheck(this, Test); }
   _createClass(Test, [{
      key: "calculatePowers",
      value: function calculatePowers() {
        return [1, 2, 3].map(function (n) {
          return Math.pow(n, 2);
        });
      }
    }]);
   return Test;
}();

Webpack

Webpack is a modular build tool that has two sets of functionality — Loaders and Plugins.

  • Loaders transform the source code of a module.
  • For example, style-loader adds CSS to DOM using style tags.
  • sass-loader compiles SASS files to CSS.
  • babel-loader transpiles JS code given the presets.

Plugins are the core of Webpack.

  • UglifyJS plugin minifies and uglifies the output of webpack.
  • We can build a simple Hello World app using Babel, SASS, Webpack, Yarn, and React.

Synopsis:

sudo npm install -g yarn

# --dev adds packages.json "DevDependencies" entry
yarn add --dev webpack babel-core babel-loader babel-preset-react
     babel-preset-es2015 node-sass css-loader sass-loader style-loader

# Only these pkgs will be installed in production.
yarn add react react-dom

=================== Source Code ==============================
src/
 /index.jsx
 /index.html
 /components
     /HelloWorld.jsx
 /styles
     /app.scss
     /components
         /hello-world.scss
==============================================================

packages.json :

    scripts: {
      "build": "webpack -p"
    }

# The build produces bundle.js file in output dir
# Can be configured by webpack config file.

# webpack converts single index.js file into bundle.js file
# by following the dependencies starting from index.js.

yarn run build   # This needs devDependencies packages.
yarn run dev     # Run dev server as defined in packages.json script.

npm install aws-amplify-angular --save
npm install aws-amplify-vue --save
npm install aws-amplify-react-native --save

The js.map and css.map files

These files contains original line information for the minified javascript/css files that would be useful for debugging in production. e.g. jquery.min.js and jquery.min.js.map file can help you generated better error message that would be useful to debug using original jquery.js later.

Duplicate Javascript Object without mutating

state  = { name : 'Raja', age: 40 }
myState ={ ...state, name: 'thava' } 

Merge 2 objects

destObj = { ..obj1, ..obj2 }
destObj = Object.assign({}, obj1, obj2)

Also See: my_array.filter(), my_array.map() returns duplicated values.

Using ES6 import in node.js

yarn add esm
node -r esm myfile.js

Promise, async, await

promise mechanism enables to execute the function and delay the callback logic much later :

promise = execute async function ...
...
promise.then( callback func which does something )
....
promise.then( another callback func for same event )

let promise = new Promise(function(resolve, reject) {

 // the function is executed automatically when the promise is constructed
 // after 1 second signal that the job is done with the result "done"
    setTimeout(() => resolve("done"), 1000);
}

Promise constructor executes given function passing it's internal
implementation of resolve and reject. We never implement resolve().

The resulting promise object has internal properties:

state — initially "pending", then fulfilled | rejected | settled"
result — Anything string or number etc. initially undefined.

// .then() synchronously waits for the result.
promise.then( result => alert(result), // shows "done!" after 1 second

The async mechanism is even better as it helps to create multiple promises on demand. The async function return values are automatically wrapped by resolved promise value :

async function f() {
   return 1;
}

f().then(alert); // 1

async function f() {
  return Promise.resolve(1);   // same as return 1
}

f().then(alert); // 1

// works only inside async functions
let value = await promise;

rxjs Observables

RxJS combines Observables and Operators so we can subscribe to streams and react to changes using composable operations.

rx = require('rxjs')
$scope.counter = 0;
rx.Observable
 .interval(1000)           // Creates interval stream of 0, 1, 2, ...
 .take(3)                  // Takes only first 3 in the stream.
 .safeApply($scope, function(x) {   // Stream operator
   $scope.counter = x;
 })
 .subscribe(); // shows 0, 1, 2  // Triggers Action.

An Observer can do actions on Observable :

var observer = rx.Observer.create(
    function onNext(result){ 
      console.log(result); 
    },
    function onError(err){ 
      console.log(err); 
    },
    function onCompleted(){ 
      console.log('Completed'); 
    }
);
observable.subscribe(observer);


// Since version 2.2, RxJS integrates with Promises using
// Rx.Observable.fromPromise.
//
// Operators like create, fromPromise, fromEvent etc creates Observables 
// given promise or event object, etc.

Higher Order Component - HOC

Concretely, a higher-order component is a function that takes a component and returns a new component.

Example:

# In App.js, import the withAuthenticator HOC at the top of the file:
import { withAuthenticator } from 'aws-amplify-react'

# And, at the bottom, update the export statement to wrap the App component
export default withAuthenticator(App)

Free MongoDB hosting