Node.js Crash Course

Introduction

I’ve been doing Node full-time at work and noticed a lot of other people lacking a centralized resource to get up and running quickly. There are a lot of wonderful resources out there for Node, a Google search away, but hopefully this document should get you coding quickly as well as able to communicate effectively with other Node developers.

I’ve tried to write this list in order of most important things you need to know. Feel free to skip around.

Contents

Node Version Manager (NVM)

Node changes often. To quickly change which version you’re using, install NVM. I’ve put nvm use stable in my .bash_profile so whenever I open a terminal, it uses the latest. If this is confusing or doesn’t work, simply download the latest installable from nodejs.org.

Running and Testing Node

Open a command line and type node. To get out, on your keyboard press Control + C. While in the Node terminal, you can write JavaScript, and import modules to test them.

To run a JavaScript file, simply cd to the directory of the code, and in your commandline type node yourfile.js.

Modules

To share code, you use modules. There are 2 types of modules: CommonJS and ES6 (ignore AMD for now). CommonJS is what Node started with, and the Browsers adopted ES6. CommonJS modules end with a .js file extension, and ES6 modules end with a .mjs file extension. Node 9 will officially adopt ES6 modules, but for now, ignore that and focus on CommonJS modules since they work with Node old and new.

The easiest way is to define functions/variables, then at the very bottom, put ’em in a list.

Simple

// a function and a variable
const cow = () => 'cow';
const AGE = 38;

// every Node.js file gets this module.exports global object
// There are a variety of ways to use it, but the easiest
// is to define an object, and put your variables in functions in it
// at the bottom of the file
module.exports = {
  cow,
  AGE
};

// use it in index.js
const { cow, AGE } = require('./cow');
console.log("cow:", cow());

Big One

When you have a higher module that imports a bunch of child ones, you can either enforce the user to require subfolders like:

const Maybe = require('./some/deep/folder/thing.js');

Which is fine. Another way to suggest what the user should use is to only expose that in a higher module:

// your library/index.js
const Maybe = require('./library/some/deep/folder/thing.js');
const CHICKEN = 'Sooo Goood, mannnn....';
const { cow, AGE } = require('./cow');

// we don't expose AGE, but everything else is ok.
// we also organize things
module.exports = {
  functional: { Maybe },
  animals: { CHICKEN, cow }
};

// use it in index.js
const yourLib = require('./library');
const result = yourLib.animals.cow();
console.log("result:", result); // cow

What is these weird, empty functions at the top of my file?

If you see things like:

(function() {
...
})();

Where the majority of the code is in the … part, that’s called an Immediately Invoked Function Expression. It’s an old pattern used in client side/browser, and is not needed nor used in Node. Simply remove the top / bottom parts, and manually expose the functions/variables you wish to use.

Modify Dependency Injection

If you see this version:

(function(window, undefined) {

That’s a browser way if doing dependency injection, specifically to help remove global variables. While things like window and document are globals, global variables make things hard to test, so this allows you to pass those values in at test & runtime. Refactor the functions that use those globals as function parameters.

Functions vs. Arrow Functions

There are a variety of ways to define functions in JavaScript. The 2 most common ways in Node are old sk00l function declarations and arrow functions.

Old Functions

The old way of defining functions is:

function nameOfIt()
{
  ... 
}

The powers that these named function declarations have is:

  1. you can forward reference them, meaning you can call them from higher up in the code before they’re actually defined in the file if you write imperative code
  2. they have a built in arguments property that is an array of the arguments passed into the function
  3. they have a this keyword which allows various forms of Object Orientated Programming
  4. older browsers provide more informative stack traces because the function name is included in the stack trace vs. an anonymous function (Node doesn’t have this problem)

Arrow Functions

None of those things are needed anymore, ecspecially in Node where stateful OOP doesn’t really exist in stateless server applications. That said, many developers still use classes.

Arrow functions have the following differences:

  1. no this, instead they adopt whatever scope they are in. If you never use this or scope, then you have none of those problems.
  2. no arguments property. If you wish to use something like that, you can define a function using rest paramereters, like addNumbers(...numbers) and the numbers property is now an Array of arguments, if any, sent, else an empty Array.
  3. They are treated like anonymous JavaScript functions, which means they are normal variables and you cannot forward reference them. That problem only occurs if you write imperative code. Calling an one arrow function from another arrow function works fine.
  4. They automatically return values unless you add {} to the function block. This removal of the need to manually write return combined with the removal of the need to write the word function leads to much smaller functions.

You should use Arrow Functions unless you know why you should be using older functions.

Arrow Functions with 1 parameter

Typically you write an Arrow function with a parameter like this:

const sayName = (name) => console.log("Hello " + name);

However, if you just have 1 argument, the () are optional:

const sayName = name => console.log("Hello " + name);

Arrow Function Line Length

While smaller, 2 new problems are created using lots of arrow functions. The first is, you can still have line length get pretty long as you try to put everything on one line. Some ESLint rules written by jerks yell about this. The second is you’ll start having functions return functions, ecspecially with Promises, and it gets unweidly to read.

You cannot break them to multiple lines after the equal:

// wrong
const sayName =
   name => console.log("Hello " + name);

But you can line break after the fat arrow:

// correct
const sayName = name =>
   console.log("Hello " + name);

This helps with nested functions since we don’t have pipe operators like Elm or Elixir.

Common Pitfall

Debugging 1 line arrow functions that are composed together can be a bit challenging. You have 3 options here.

const add = (a, b) => a + b;

The first is to break it out to a multiple line, imperative style function:

const add = (a, b) => {
   console.log("a:", a);
   console.log("b:", b);
   const result = a + b;
   console.log("result:", result);
   return result;
};

The challenge is to remember to use the return keyword to return the result once you go back to multiple line arrow functions.

The second option is to use an || (or) statement to log your info first. Since console is a noop (function that returns no value), it’ll return undefined, and trigger the code to the right of the || operator.

const add = (a, b) => console.log("a:", a) || a + b;

The third option is to use a modern IDE like Visual Studio Code that supports adding breakpoints on columns.

Truthy / Falsey

if (thing)

JavaScript has lax operators for Boolean evaluation. In short, they suck, hence we call them “truthy” and “falsey”. They aren’t very exact.

You have 3 options:

  1. learn them and look smart, yet have to continually remind your coworkers
  2. ignore them and don’t go down that path and use Lodash

For example, this prints out “it’s true, homey”:

const cow = true;
if(cow) {
   console.log("it's true, homey");
}

So does this:

const cow = 'false';
if(cow) {
   console.log("it's true, homey");
}

The same holds true for nothing using equality vs strict equality in JavaScript:

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

Ignore it and all the weird edge cases. Create predicate functions using Lodash, and your problems go away, and your code works in all browsers and in Node versions.

Nots

You’ll occasionally see people do if(!thing) {. It basically means !== true.

Equality

Comparison operators in JavaScript are broken. You can learn the differences if you care to, but they are too hard to remember and don’t really help you write better code. Don’t use 2 equals, use 3:

// wrong
if(thing == false)
// correct
if(thing === false)

Aysnchronous Programming

JavaScript, unlike other programming languages, is asynchronous by default. Learn more to understand how to avoid creating race conditions as well as helpful tips if you come from C#. If you’re from Scala, JavaScript’s Promises are like Scala’s Futures.

In short, JavaScript does not stop or “block” on a line of code while an asynchronous operation such as an HTTP request, file read, or database call is run. Instead, you can give it a function to call later when it’s done, and your code keeps running in the current call stack. I’ve written an article that hopefully gives you clear examples about asynchronous programming.

Callbacks vs. Promises

The old way to code in Node is using callbacks. The new way is Promises. Since it is an opinion, Node continues to support callback API’s and create new API’s using callbacks. While callbacks can result in callback hell, so can Promises.

Either way, callbacks sadly are noops, meaning they don’t return a value. We don’t do that in functional programming, and neither should you. While newer versions of node support promisify, you should be using Promises because:

  1. they always return a value
  2. they have built in try/catch
  3. they are a native, finite state machine
  4. they use Left/Right functional programming error handling fall through

This leads to easier unit testing, more composable functions, and easier to debug code.

Best article to learn Promises is to learn how Promises are used wrong.

That said, if callbacks are easier for you, and you’re stuck with Promise based code, Node 9 has a way to convert them back to callbacks.

Commandline

To build command line Node apps, check out Commandeer.

Object Oriented Programming

The basics of classes with inheritance work in the latest browser and Node without the need of a transpiler/compiler using ES6. The one caveat is scope. In Java, C#, and ActionScript 3 for example, functions from a class instance retain their scope when called. However, in Lua, Python, and JavaScript, when you pass functions around, they don’t retain what class they are attached to. The quick fix is to use bind.

However, transpilers offer a lot of nice features that, if you’re from an OOP background, it’s worth your time to check out.

For the basics, check out the Babel compiler. For a language, compiler, and simpler parallelism functionality, with runtime exceptions for non-prod code, check out Google’s Dart. For another great typed language with a helpful compiler, check out Microsoft’s TypeScript. Facebook has Flow in much the same vein.

Be aware, a lot of the marketing of the above tools target browser developers, but many work fine for Node. The beauty of Node is you “can just write code and run it” without waiting for a recompile, but for many, this isn’t a problem.

Functional Programming

You have 2 options: use libraries or a transpiler.

For libraries Folktale v2 follows the Fantasy Land spec. Lodash has both functional methods as well as array comprehensions, and low-level JavaScript predicates. Ramda has many wonderful methods as well.

If the mutable state and impurity of JavaScript is too much, you can use Facebooks’ OCAML influenced Reason, or Haskell influenced PureScript.

Unit & Integration Testing

To unit test, the 4 main test runners are Tape, Jasmine, Mocha, and Jest. I like Mocha. Mocha has an assertion library, Chai. For code coverage, use Istanbul. To prevent unit tests accidentally become integration tests making HTTP callsĀ  and other http exceptions, use Nock. For mocking & spies (you poor thing) use Sinon. For integration testing, check out Supertest.

Debugging

You have 2 options.

The first is to use the variety of console options.

My favorite is to pass objects with a String label in console.log like:

console.log("person:", person);

The second is to use breakpoints. My old manager, Brian Clark, has a great post on setting up Node debugging.

One Reply to “Node.js Crash Course”

Comments are closed.