''' Node JS tutorial | Modern JS

Node JS tutorial

Node JS -- also known as Node.js -- is practically at the center of all modern JavaScript development. Node JS plays such a critical role in the modern JavaScript ecosystem because it's used to run JavaScript, and not just any JavaScript like the kind browsers run on a day to day basis, but rather more advanced JavaScript (e.g. of the networking and file system type). In addition, Node JS is also designed to administer JavaScript packages, which in turn allow Node JS to run more complex JavaScript applications built on these JavaScript packages.

Before jumping into how Node JS works, let's take a quick look at how Node JS compares to other software you've probably worked with, namely JavaScript browser engines and other programming language run-time environments, in this manner, you'll gain a better understanding of the tasks Node JS is designed to perform.

How Node JS compares to JavaScript browser engines and other programming language run-time environments.

In Modern JavaScript essentials, I mentioned how the various mass-market browsers (e.g. Google Chrome, Microsoft Edge, Firefox, Apple Safari) are equipped with different JavaScript engines to support the execution of JavaScript or more specifically ECMAScript, the last of which is the standard that gives form to the JavaScript language.

All these JavaScript engines are great for running JavaScript on a browser, but you have to remember these JavaScript engines are pegged to ECMAScript standards. So what's the problem with ECMAScript standards ? They tend to be limited in terms of functionalities. Although ECMAScript standards have evolved to support ever increasing features -- as you can review in the previous sections: JavaScript key concepts & syntax, JavaScript data types, JavaScript object-orientated and prototype-based programming, JavaScript for loops and JavaScript asynchronous behavior -- relying solely on ECMAScript standards means the JavaScript language is pretty constrained, particularly if you compare it to other programming languages and their run-time environments.

In most programming languages like Python, PHP or Ruby, when you install their run-time environments, an installation is not only equipped to run the entire set of instructions available in a programming language, it's also equipped with a set of staple libraries/modules to execute run of the mill programming tasks (e.g. read/write files, establish network connections), as well as the ability to leverage third party libraries/modules to execute practically any kind of programming tasks. This type of architecture which is available out-of-the-box in most programming language run-times, is one of the major voids Node JS fills over JavaScript browser engines and is why Node JS has become such a dominant player in the modern JavaScript ecosystem.

If you view Node JS from the perspective of other programming language run-time environments, Node JS is like the JavaScript run-time environment that should have been, because it resembles what most programming language run-times offer out-of-the-box. Figure 6-1 illustrates the resemblance of a Node JS installation to a Python installation.

Figure 6-1. Node JS installation vs. Python installation

When you perform a Python installation it comes equipped with the features you see to the right side of figure 6-1. For starters, a Python installation allows the execution of the Python language, but in addition comes equipped with a set of handy Python modules to execute common programming tasks in Python (e.g. read/write files, establish network connections). In addition, a Python installation also comes equipped with a package manager -- named pip -- designed for the installation and management of third party Python packages to aid in the execution of more advanced programming tasks in Python (e.g. web frameworks, business analytics).

On the left side of figure 6-1, you can see that a Node JS installation at its core uses the same V8 JavaScript engine built-in to the Google Chrome browser. So does this mean Node JS is like a browser ? No, Node JS leverages the same V8 JavaScript engine used by a browser to process JavaScript, but this is the only thing Node JS has in common with a browser. In figure 6-1 you can also see a Node JS installation comes equipped with a set of handy JavaScript modules[1] to execute common programming tasks in JavaScript. In addition, a Node JS installation also comes equipped with a package manager -- named npm -- designed for the installation and management of third party JavaScript packages to aid in the execution of more advanced programming tasks in JavaScript (e.g. web frameworks, business analytics).

With this overview of what constitutes a Node JS installation, you can understand why the built-in JavaScript installations of mass-market browsers with their more limited functionalities have never been an option for modern JavaScript development. It required looking beyond the features offered by built-in JavaScript installations of mass-market browsers and getting insight from what other programming languages offered out-of-the-box, for Node JS to come into existence and become a dominant force in modern JavaScript projects.

Node JS - Installation and versioning

Since Node JS is a core piece of software that runs JavaScript, like all other core pieces of software that run programming languages, it's generally very easy to install, but it can have a myriad of behavioral differences if you don't use a specific version for a given JavaScript application.

Node JS has been released in over twelve major versions, with even numbered versions (e.g. 12, 10, 8) representing Long Term Support (LTS) releases -- which means they're maintained for a longer period of time, approximately 30 months -- where as odd numbered versions (e.g. 11, 9, 7) have quicker End of Life (EOL) timespans -- which means as soon as a new version is released, approximately every 6 months, the prior version is no longer updated. The finer details of the Node JS version release strategy[2] can be a little complex to follow, so for practical purposes, I recommend you stick to using Node JS LTS releases or whatever Node JS version the provider of a given JavaScript application recommends.

At the time of this writing, Node JS 12 is the latest LTS release, so any steps outlined from this point on are based on the use of Node JS 12. I can't emphasize enough how using Node JS 12 is no guarantee that it will work on all software that requires Node JS. In fact, from personal experience I can say that sometimes even minor Node JS version variations (e.g. 10.1.0 to 10.8.0) can break software, never mind major Node JS version variations (e.g. 8 to 10 or 10 to 12), so if a piece of JavaScript software recommends using Node JS version x.x, use that Node JS version to avoid any unexpected behaviors.

When it comes to installation, Node JS is available for many operating systems and processor architectures[3], in addition to being available in source code so it can also be built to run on any platform. In addition, some operating systems have turn-key installation support for Node JS through their package managers (e.g. apt, rpm), albeit this last approach rarely supports the most up to date Node JS version, so it's often best to directly download a specific Node JS installation package instead of relying on an operating system package manager.

In most cases, a few mouse clicks or command line instructions will be sufficient to install Node JS. But in case you get stuck during the installation process, I advise you to look over other resources on the web for a possible solution to your installation problems, as covering Node JS installation problems would go beyond the scope of how Node JS works. Being such a widely adopted platform, it's very likely someone else has encountered and documented a possible solution to a given Node JS installation problem.

Once you successfully install Node JS, it will have a bin folder with the following executables:

To make your life easier, I suggest you add the Node JS bin folder to your operating system's PATH environment variable[4][5] so the Node JS executables node, npm and npx become available anywhere on your operating system.

The Node JS node command

The node command is the main Node JS executable and is one of three major binaries included with Node JS. If you have a background in another programming language, you can think of the node executable as the equivalent to a javascript executable, similar to Python's python executable or PHP's php executable which are at the center of each programming language's actions.

Like all other main executables in programming language run-time environments, the Node JS node command has a wealth of options and environment variables you can use to modify its default behavior, which include behaviors for debugging, security, profiling and experimental features, among other things.

If you execute node with the --help flag (e.g. node --help) you'll see the list of over fifty Node JS options and over fifteen Node JS environment variables. The majority of the time you'll only use a few of these options, which are the ones I'll describe in the upcoming sections.

A JavaScript REPL

REPLs are a common tool in many programming languages to test out language statements without the need to perform complex setups. The node REPL mode provides access to a JavaScript environment in which you can interactively evaluate JavaScript statements.

Go ahead and execute node without any arguments to enter the REPL mode as illustrated in listing 6-1.

Listing 6-1. node REPL mode
[user@laptop]$ node
Welcome to Node.js v12.2.0.
Type ".help" for more information.
> .help
.break    Sometimes you get stuck, this gets you out
.clear    Alias for .break
.editor   Enter editor mode
.exit     Exit the repl
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file

Press ^C to abort current expression, ^D to exit the repl
>

When you execute node it displays the Node JS version, followed by a help message and presents a prompt > awaiting further instructions. Go ahead and type .help, which as shown in listing 6-1 further displays other commands available in the node REPL mode.

At this point, you can enter any JavaScript statement at the node REPL prompt to be evaluated. Listing 6-2 contains a series of JavaScript statements you can try out.

Listing 6-2. JavaScript statements evaluated in node REPL mode
> Math.PI
3.141592653589793
> 2**5
32
> let letter = 'a'
undefined
> let echoer = function(message) { 
   return message;
}
undefined
> echoer(letter)
'a'
> process.versions.v8
'7.4.288.21-node.17' 

The first JavaScript statement Math.PI in listing 6-2 evaluates to 3.141592653589793, what's interesting about this statement is not so much the result, but rather that the Node JS REPL provides access to the JavaScript built-in Math data type. The second JavaScript statement 2**5 ("2 to the power 5") in listing 6-2 evaluates to 32, here again the mathematical result isn't what's interesting, but rather the use of the JavaScript exponentiation operator ** which is a new syntax from ECMAScript2016 (ES7), confirming the Node JS REPL uses a fairly updated JavaScript engine.

The third JavaScript statement let letter = 'a' is a simple JavaScript assignment statement which outputs undefined because evaluating an assignment never returns a result, however, the statement does make the letter reference available for later access. The fourth JavaScript statement let echoer is a function expression that returns whatever value is passed by a caller, which also outputs undefined because evaluating function expression never returns a result. The fifth JavaScript statement in listing 6-2 evaluates calling the echoer function expression with the letter reference, an evaluation that results in 'a' which is the value of the letter reference.

Finally, the last JavaScript statement in listing 6-2 evaluates the process.versions.v8 reference. Although the process.versions.v8 reference is a somewhat advanced Node JS topic, it provides some interesting insight. Recall that back in figure 6-1 I mentioned how a Node JS installation at its core uses the same V8 JavaScript engine built-in to the Google Chrome browser, it turns out the output of the process.versions.v8 reference indicates the V8 engine used by the Node JS installation, which in this case corresponds to V8 7.4.288.21-node.17 and corresponds to the same V8 engine used by Google Chrome Browser version 74, in accordance with the V8 version numbering scheme [6].

Now let's use some of the Node JS REPL commands illustrated in listing 6-1. After you type JavaScript statements like the ones in 6-2 you can save them to a file for posterity with the .save command illustrated in listing 6-3.

Listing 6-3. Save statements introduced in node REPL to file
> .save myscript.js
Session saved to: myscript.js
> <Type .exit or Ctrl-D with keyboard to exit>
<Analyze contents of myscript.js>

The .save myscript.js syntax in listing 6-3 tells the Node JS REPL to save all the evaluated JavaScript statements to a file named myscript.js, where myscript.js is a file in the present working directory where the Node JS REPL was started. If you exit the Node JS REPL with the .exit command or Ctrl-D keyboard combo, you'll be able to confirm the generated file contains all the JavaScript statements introduced in the REPL session.

Now let's use the Node JS REPL .load statement illustrated in listing 6-4 to demonstrate how it's possible to renew a Node JS REPL with JavaScript statements provided in a file.

Listing 6-4. Load statements in node REPL from a file
[user@laptop]$ node
Welcome to Node.js v12.2.0.
Type ".help" for more information.
> .load myscript.js
...
...
> echoer("Hello Node JS REPL!")
'Hello Node JS REPL!'

The .load myscript.js syntax in listing 6-4 tells the Node JS REPL to load the JavaScript statements from the file named myscript.js, in this case myscript.js is the file generated in listing 6-3, but it could equally be any file with valid JavaScript statements. Once the JavaScript statements are loaded into the Node JS REPL with .load, it's possible to leverage the declarations as if you'd typed them in yourself. Notice in listing 6-4 the statement echoer("Hello Node JS REPL!") outputs 'Hello Node JS REPL!' which works because the myscript.js file has a JavaScript function expression named echoer.

A JavaScript syntax checker

Although the JavaScript REPL from the last section is one of the main offerings of the node executable, this doesn't mean it's the only practical functionality it has to offer. The node executable also supports the -c or --check flags to check JavaScript syntax. To test this node feature, I recommend you purposely modify a JavaScript file to include an invalid JavaScript statement (e.g. modify a let statment to et) and run it using the process shown in listing 6-5.

Listing 6-5. Check JavaScript syntax with node -c--check flags
[user@laptop]$ node -c myscript.js
[user@laptop]$ node -c broken_script.js
/broken_script.js:4
et echoer = function(message) { 
   ^^^^^^

SyntaxError: Unexpected identifier
    at new Script (vm.js:83:7)
    at checkSyntax (internal/main/check_syntax.js:78:3)
    at internal/main/check_syntax.js:42:3

The first statment node -c myscript.js in listing 6-5 outputs nothing because the myscript.js file contains valid JavaScript statements and also because the -c flag (or --check flag) simply checks for JavaScript syntax errors without executing anything. The second statment node -c broken_script.js is run against the broken_script.js file, which you can see in the output contains a SyntaxError: Unexpected identifier in line 4 of the file (i.e. broken_script.js:4 et echoer = function(message) { ^^^^^^). As you can see from the examples presented in listing 6-5, the node executable with the -c or --check flags can be helpful to quickly pinpoint JavaScript syntax errors in files of any size.

A JavaScript evaluator

In addition to Node's interactive REPL environment, Node can also directly evaluate and print JavaScript statements -- which are the Evaluate and Print in REPL. The node executable supports the -e or --eval flags to evaluate JavaScript statements, as well as the -p or --print flags to evaluate and print JavaScript statements. Listing 6-6 illustrates how to evaluate JavaScript statement with the -e or --eval flags.

Listing 6-6. Evaluate JavaScript statement with node -e--eval flags
[user@laptop]$ node -e "Math.PI"
[user@laptop]$ node -e "console.log(Math.PI)"
3.141592653589793
[user@laptop]$ node -e "let letter='a'"
[user@laptop]$ node -e "let letter='a';console.log(letter);"
a

The first statement in listing 6-6 shows evaluating the Math.PI property results in no output, because evaluating a property never returns a result, however, the next statement "console.log(Math.PI)" does print 3.141592653589793 since evaluating console.log outputs its enclosed contents, which in this case is the Math.PI property value. The third statement in listing 6-6 illustrates that evaluating an assignment doesn't return a result, where as the fourth statement once again makes use of console.log to print the reference of the assignment statement.

Listing 6-7 illustrates how to evaluate and print JavaScript statement with the -p or --print flags.

Listing 6-7. Evaluate and print JavaScript statement with node -p--print flags
[user@laptop]$ node -p "Math.PI"
3.141592653589793
[user@laptop]$ node -p "let letter='a';letter;"
a

The examples in listing 6-7 are similar to those in listing 6-6, but notice the ones in listing 6-7 don't use console.log and still print a result. The reason for this behavior is because the -p and --print flags both evaluate and print statements. Therefore, the result of evaluating and printing the Math.PI property is 3.141592653589793 and the result of evaluating and printing the let letter='a';letter; statement is a, none of which require the use of console.log statements, since the -p and --print flags automatically print their output.

Node JS - A JavaScript CommonJS based system

The previous exercises using the node command might give you the impression the Node JS JavaScript run-time environment functions just like the one in mass-market browsers, specifically Google Chrome's V8 engine which is the one used by Node JS. This impression would be partially correct, because although standard JavaScript statements do work the same in both because they use the V8 engine, the Node JS JavaScript run-time environment does in fact work differently due to its use of JavaScript CommonJS.

As early as the Modern JavaScript essentials section, I mentioned how Modules, namespaces & module types were among the most important and also among the most fragmented techniques in modern JavaScript.

Node JS uses the oldest of the JavaScript module standards, CommonJS, for reasons that have more to do with 'what was avaialble at time' than anything else. If you've never worked with JavaScript modules, I recommend you review the link in the previous paragraph on modules, as well as the ES5: Function declarations, function expressions, and immediately-invoked function expressions (IIFE) section, ES6: Modules, namespaces, export and import section, in addition to the Pre-ES6 modules detour: CommonJS, AMD and UMD section, to gain a better understanding of JavaScript modules in general.

What CommonJS brings to Node JS is the ability to use namespaces and avoid name clashes when running JavaScript statements from different modules, which for practical purposes modules generally equals .js files. It might not have been obvious in the previous node REPL exercises, but in Node JS, every JavaScript statement belongs to a namespace to protect it from conflicting with references in other JavaScript modules.

The Node JS global object

In the concept section on JavaScript data types, I mentioned how JavaScript engines rely on a global object to keep track of references when they don't have an explicit scope. Recapping, I described how browsers use the window keyword as their global object to store a browser's built-in global references (e.g. eval(), alert()) and any other statement created in the global scope, as well how it was redundant -- most times -- to make explicit use of the window global object reference (i.e. window.alert() produces the same result as alert()). In that same section, I also mentioned how Node JS relies on the global keyword to manage its JavaScript engine global object, which we're going to take a closer look at next.

  1. https://nodejs.org/api/modules.html    

  2. https://github.com/nodejs/release    

  3. https://nodejs.org/en/download/    

  4. https://www.windows-commandline.com/set-path-command-line/    

  5. https://opensource.com/article/17/6/set-path-linux    

  6. https://v8.dev/docs/version-numbers