Mike Slinn
Mike Slinn

Node.js, NVM, NPM and Yarn

Published 2022-03-01. Last modified 2023-01-26.
Time to read: 4 minutes.

This page is part of the posts collection, categorized under JavaScript.

Node.js, a JavaScript runtime, is not my favorite programming environment. Originally released 13 years ago by Ryan Dahl, a software engineer at Google, it’s historical disregard for security and stability is problematic.

However, node.js has significant traction, especially amongst Millennial software technologists, and is used by many Ruby and Scala projects for HTML asset pipelines.

I put together these notes for installing and maintaining node.js on Ubuntu/WSL using a virtualized version manager.

Why Virtualize Node.js?

It is better to use virtualized user- and project-specific node.js instances, instead of working with a system-wide installation of node.js. This allows you to install and upgrade node.js packages without using supervisor privileges. Also, virtualized instances allows you to work on many different independent node.js projects at the same time, without package version collisions.

Docker is over-sold. It adds unnecessary complexity to software projects. Instead of virtualizing the entire software environment, as docker attempts to do, virtualizing the programming environment with node.js nvm, Python venv, or Ruby rbenv are much easier and more productive approaches.

I think docker has been pushed hard in the media because it is a gateway technology to PaSS. This is a trend that PaSS vendors like AWS and Azure want to encourage, but customers are pushing back.

Nvm

Nvm, the Node Version Manager, makes it easy to install multiple virtualized node.js instances, and to easily switch between them. Nvm retains a unique set of installed packages for each node.js instance. The node.js version in each instance is distinct.

Nvm is installed and updated as follows:

Shell
$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 14926  100 14926    0     0  84806      0 --:--:-- --:--:-- --:--:-- 85291
=> nvm is already installed in /home/mslinn/.nvm, trying to update using git
=> => Compressing and cleaning up git repository

=> nvm source string already in /home/mslinn/.bashrc
=> bash_completion source string already in /home/mslinn/.bashrc
=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion 

View the currently installed versions of node.js:

Shell
$ nvm list
->       system
iojs -> N/A (default)
node -> stable (-> N/A) (default)
unstable -> N/A (default) 

View the very long list of available versions of node.js like this:

Shell
$ nvm list-remote
          v17.7.0
        v17.7.1
        v17.7.2
        v17.8.0
        v17.9.0
        v17.9.1
        v18.0.0
        v18.1.0
        v18.2.0
        v18.3.0
        v18.4.0
        v18.5.0
        v18.6.0
        v18.7.0
        v18.8.0
        v18.9.0
        v18.9.1
       v18.10.0
       v18.11.0
       v18.12.0   (LTS: Hydrogen)
       v18.12.1   (LTS: Hydrogen)
       v18.13.0   (Latest LTS: Hydrogen)
        v19.0.0
        v19.0.1
        v19.1.0
        v19.2.0
        v19.3.0
        v19.4.0
        v19.5.0 

Using Nvm to Install Node.js

Install the latest release of node.js using nvm:

Shell
$ nvm install node
Downloading and installing node v19.5.0...
Downloading https://nodejs.org/dist/v19.5.0/node-v19.5.0-linux-x64.tar.xz...
##################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v19.5.0 (npm v9.3.1)
Creating default alias: default -> node (-> v19.5.0) 

In the above example, node is an alias for “the latest version of node.js”. To install a specific version of node.js, for example 18.13.0:

Shell
$ nvm install 18.13.0

Yarn

Needle and thread under an electron microscope
Needle and thread under an electron microscope

Whether you use nvm as your node.js virtualization mechanism, you also need a node.js package manager to install and maintain project dependencies.

Yarn supposedly stands for Yet Another Resource Negotiator, and it is a package manager like npm, described next. It was developed by Facebook and is now open-source. Yarn was developed to address npm’s performance and security issues.

The name actually suggests the major advantage yarn has over its predecessor, npm: multi-threading instead of single-threading.

Yarn generates a yarn.lock file, which helps easy merges. The merges are predictable. Yarn caches every package it has downloaded, so it never needs to download the same package again. It also does almost everything concurrently to maximize resource utilization. This means faster installs. Yarn guarantees that any installation that works on one system will work exactly the same on another system, unlike npm.

The notes for the yarn package state that Yarn was inspired by Bundler and Cargo, and is nearly command-line compatible with npm.

Yarn introduces the zero-install concept, which means that a project should be able to be used as soon as it is cloned. It uses Plug’n’Play to resolve dependencies via the yarn cache folder and not from node_modules. The cache folder is by default stored within your project folder, in .yarn/cache.

Installing Yarn

To install yarn on Ubuntu:

Shell
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | \
  sudo apt-key add -

$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | \
  sudo tee /etc/apt/sources.list.d/yarn.list

$ sudo apt update && sudo apt install yarn

Configuring a Project With Yarn

The yarn init command initiates an interactive session that creates a package.json file.

Shell
$ cd /path/to/my/node/project

$ yarn init
yarn init v1.22.19
question name (node): blah
question version (1.0.0): 0.1.0
question description: Just a little song I wrote
question entry point (index.js):
question repository url: https://github.com/mslinn/blah
question author: Mike Slinn
question license (MIT):
question private:
success Saved package.json
Done in 48.29s. 

The above dialog creates package.json in the current directory:

package.json
{
  "name": "blah",
  "version": "0.1.0",
  "description": "Just a little song I wrote",
  "main": "index.js",
  "repository": "https://github.com/mslinn/blah",
  "author": "Mike Slinn",
  "license": "MIT"
}

Adding a Dependency to a Project

Yarn stores dependencies locally. If the proper version is present locally, it is fetched from the disk during a yarn add command, otherwise it is downloaded. This is the general format of the command to add a dependency to a node.js project using yarn:

Shell
$ yarn add package_name

To add a specific version of a package:

Shell
$ yarn add package_name@version_number

To install a global package, the general formats are:

Shell
$ yarn global add package_name

$ yarn global add package_name@version_number

Yarn and Git

The official Yarn docs say to add the following to .gitignore for git projects that use yarn:
.gitignore
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

Installing Dependencies

To install all the dependencies in a node.js project, type yarn or yarn install:

shell – Updating All Dependencies With Yarn
$ cd /path/to/my/project

$ yarn

Updating Dependencies

To upgrade all the dependencies in a node.js project, use yarn upgrade:

shell – Updating All Dependencies With Yarn
$ cd /path/to/my/project

$ yarn upgrade

Npm

Npm is the original package manager for the family of JavaScript programming languages, and it is still the default package manager for node.js. This will likely change as yarn matures. Npm helps install libraries, plugins, frameworks and applications. It consists of a command-line client, also called npm, and an online database of public and paid-for private packages called the npm registry.

If you are using yarn then you do not need to use npm.

Npm fetches dependencies from the npm registry for every npm install command.

Npm generates a package-lock.json file. The layout of this file is a trade-off between determinism and simplicity. The same node_modules/ folder will be generated by every version of npm. Every dependency will have a version number associated with it in the package-lock file.

Npm vs. Yarn

When using npm install, dependencies are installed sequentially, one after another. The output logs in the terminal are informative but a bit hard to read. To install the packages with Yarn, run the yarn command. Yarn installs packages in parallel, which is one of the reasons it is quicker than npm.

For more information, see Difference between npm and yarn.

Install A Global Package

To install a global package, the command template for npm is:

Shell
$ npm install -g package_name[@version_number]

For example:

Shell
$ npm install -g broken-link-checker
npm WARN deprecated calmcard@0.1.1: no longer maintained
npm WARN deprecated nopter@0.3.0: try optionator
npm WARN deprecated uuid@2.0.3: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated urlobj@0.0.11: use universal-url, minurl, relateurl, url-relation

added 104 packages, and audited 105 packages in 5s