Programmers' Guide to NodeJS
NodeJS is a popular, open source JavaScript runtime environment. In this guide for programmers, our expert explains how to use NodeJS and what NodeJS is used for.
Why Use NodeJS?
Speaking as an old-school enterprise Java programmer, I often feel like I need to make two separate confessions when I talk to people about NodeJS. First, I have to confess to my peers that I really, really like NodeJS. This often gets mixed reactions ranging from excited agreement to outright disgust. Second, I have to confess that I really didn’t like it at first, even though I’ve been working with Javascript since the late 90s. This runs the risk of scaring people off of a platform that I’ve grown to love.
Examining my own feelings on the subject, I realized that a lot of my early aversion to NodeJS was really borne out of the language asking me to do things that I had deemed unseemly practice in more disciplined languages like Java. If I’m being 100% accurate, my first hesitation was even earlier than that – it was the very act of trusting an ‘undisciplined’ language like Javascript to control my precious and critical backend logic!
Back to topNodeJS Guide: When, Why, and How to Use NodeJS
As I began to explore the language, and perhaps more importantly its architecture and ecosystem, I realized that I was simply asking NodeJS to be something it was not. I was asking NodeJS to be an old school language. It didn’t take much of a leap then for me to realize that the problem was with me, and that what the world really needs is an old school programmers' guide to NodeJS.
A proper guide could fill several volumes, and maybe someday it will. In the meantime, I want to at least highlight some of the exemplifying and/or unique aspects of NodeJS which might be problematic at first to those new to the language, and which were most certainly problematic for someone with my kind of background.
Back to topAsynchronous vs. Synchronous Workflow
The first impasse that most old school developers reach when they encounter NodeJS is the inherently asynchronous patterns. For developers that are used to doing things in order, it can be frustrating to not be allowed to take for granted that NodeJS doesn’t want one part of the app to “care” about the progress of another part of the app.
But, what do Twelve-Factor apps tell us about the way we should think of our processes? The Twelve-Factor app tells us that we should think of our processes as isolated, stateless processes.
Why? We want those processes to be ephemerally executed, capable of being passed from one execution environment to another seamlessly without interruption to program workflow. Why again? So that our apps can execute as much of their workflow in parallel, and therefore across as many horizontally scaled compute resources as necessary.
These days increases in compute capacity are not originating from advancements in processor clock speed, but rather in the number of individual cores that we can shove into to a board.
In order to take advantage of that additional capacity our applications must be capable of utilizing those cores simultaneously rather than waiting on individual components to finish their work.
NodeJS's Architecture Event Loop "Libuv"
At the core of NodeJS’s architecture is an event loop library called “libuv.” Libuv is responsible for interpreting units of Javascript work from the code provided by the developer and queueing those requests as asynchronous events.
Each unit of work is executed in its own isolated instance of the V8 Javascript Engine, and so you achieve basic thread safety. This does happen at the cost of having to provide meta information along with each unit of work, but, for the most part NodeJS hides this from you and you don’t need to worry about it.
Even though the benefits of building a language that does this inherently may now be clearer, that doesn’t make it any easier for newcomers to deal with asynchronous programming semantically. The EventEmitter class can make this experience much easier. Rather than nesting functions and callbacks, you can treat each of your work units as events.
var events = require('events');
var eventEmitter = new events.EventEmitter();
var ringPhone= function ringPhone() {
console.log(’brrrrrr-ring, brrrr-ing, brrrrring…’ );
}
eventEmitter.on(’incomingCall’, ringPhone);
eventEmitter.emit(‘incomingCall');
The more recently introduce async/await functionality can also help developers who are used to a more procedural syntax:
const getData = () => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => console.log(json));
};
It can then become:
const getDataAsync = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
console.log(data);
};
Back to topObjects and Typing in Node.js
Javascript is simultaneously a functional language and an object-oriented language. In NodeJS, you can create classes like you are used to in Java, and you can also treat functions as first-class citizens, i.e. you can pass them as if you were just passing a normal variable:
function callMeFirst(callMeLater) {
callMeLater('my first message');
}
callMeFirst(
(message) => { console.log(message) } This is an ‘anonymous’ function being passed
);
I find it’s helpful to just practice coding with a debugger. It takes some getting used to creating breakpoints on your code and introspecting them to see the function and meta data structure that NodeJS uses to encapsulate your functions.
If you think if that structure as being essentially just an Object, and that Object contains some executable and evaluatable Javascript code as well as some meta-information that identifies and helps manage the function, it’s a bit easier to wrap your head around:
Another thing to consider coming from Java is that Javascript is a loosely typed language. This is where your debugger can really help you again. The meta-information that goes into the values you are watching will tell you exactly what kind of object you are dealing with.
If you really can’t take the loose typing, though, be aware that Javascript does support a “strict” mode which will take things into account such as:
- Safe variable assignment.
- Restricting access to the global context.
- Defining duplicate properties in an object.
You can add “use strict” to the top of your code to turn this mode on for your entire script, or, you can even embed it in single functions to scope strict checking to those functions.
Finally, like any other language, there is no replacement for mindful, comprehensive exception checking. Be aware of the calculations you are performing and don’t take for granted that all calculations will be valid or that data will be properly converted by the time it makes it to your code. Well written code will plan for the unexpected and use try/catch blocks to plan contingent logic.
Back to topIDE Considerations
Like any programming language you may be used to, NodeJS can be 100% programmed using nothing but patience, caffeine, and VIM. However, most veteran developers have come to appreciate the benefits of a well-designed IDE.
Microsoft’s cross-platform open source Visual Studio Code (VSCode) is my personal IDE of choice when it comes to Node.js. Build and run configurations are very easy to maintain, the code debugger is seamless and easy to use, and the interface itself is lightweight, efficient, and visually pleasing.
Most of the editor’s configuration is done in JSON and so use with any Javascript-derived framework including NodeJS will offer a consistent experience across the board. All expected SCM interoperability such as Git or Perforce Helix Core, is implemented well.
This is all in addition to the wealth of extensions and plugins provided by the NodeJS community at large:
This is by no means your only option, with plenty of IDEs to choose from, including offerings from Java-friendly organizations like Eclipse and IntelliJ.
Back to topManaging Dependencies in Node.Js
Those who have grown accustomed to Java build management tools such as Maven and Gradle will be pleased to know that an extensive package manager called ‘npm’ exists for NodeJS and can be easily invoked to deal with dependencies.
NodeJS applications that have dependencies, per good practice, should ship with a build description file called package.json. This file will contain information about the build itself including meta data like the title and version, and dependency information.
{
"name": "helloworld",
"version": "1.0.0",
"description": "Hello World!",
"main": "hello.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Justin Reock",
"license": "GPL-3.0-or-later"
}
When deploying a new NodeJS application, it is common to only distribute the package.json file and the bespoke source code for the application. The ‘npm install’ command can be run from the root installation folder of the app and all of the prescribed dependencies for the application will be downloaded and aligned.
Note that it is trivial to build local, private repositories for NodeJS, similar to what you may be used to with tools such as Artifactory and Sonatype Nexus. Npm even allows you to build semantic versioning into your build processes, allowing for further automation of things like automatic dependency patching of non-breaking releases. Paired with new methodologies for achieving constant feedback loops and canary releases, this can make for incredibly robust, safe and self-updating dependency relationships.
All NodeJS developers should originate their NodeJS apps with a call to ‘npm init’ which will create a starter package.json file after a few prompts. From there, the ‘npm install’ directive can be used to add new dependencies which will be immediately available for coding against.
NodeJS Deserves a Place in the Enterprise
I hope that this has helped to illuminate for you that moving to NodeJS doesn’t mean eschewing all of the familiar concepts that veteran coders have come to appreciate. Though different in many respects, NodeJS provides ways to address the programming styles of both new and veteran developers.
NodeJS vs. Python
The main difference between NodeJS vs. Python is that NodeJS is typically a better choice for web applications and development. Python, on the other hand, can be used for a broader variety of purposes, including machine learning, back-end application integration, numerical computation, and more.
Get Help With NodeJS
NodeJS can be a great fit for the enterprise, but you might need help to make it work for your business. That's where the OpenLogic comes in.
Our experts are knowledgeable in all things Java, including NodeJS. We can help you:
- Set up a workflow
- Use objects and typing
- Manage dependencies
- And more!