Published 2022-03-01.
Last modified 2025-03-12.
Time to read: 4 minutes.
posts collection, categorized under JavaScript.
Node.js,
a JavaScript runtime, is not my favorite programming environment.
Originally released 16 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:
$ 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 $ export NVM_DIR="$HOME/.nvm"
$ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
$ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
The above clones the github.com/nvm-sh/nvm project to ~/.nvm.
View the currently installed versions of node.js:
$ 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:
$ 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:
$ 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:
$ nvm install 18.13.0
Yarn
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:
$ 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.
$ 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:
{
"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:
$ yarn add package_name
To add a specific version of a package:
$ yarn add package_name@version_number
To install a global package, the general formats are:
$ 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:
.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:
$ cd /path/to/my/project $ yarn
Updating Dependencies
To upgrade all the dependencies in a node.js project,
use yarn upgrade:
$ 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.
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 Npm With a Node Version Manager
Follow these instructions. In summary:
$ mkdir ~/.npm-global
$ npm config set prefix ~/.npm-global
$ cat >> ~/.bashrc <<EOF export PATH="$HOME/.npm-global/bin:$PATH"
export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" 1>&2 # Loads nvm EOF
$ source ~/.bashrc
$ curl -fsSL https://deb.nodesource.com/setup_21.x | \ sudo -E bash - && \ sudo apt-get install -y nodejs
Install A Global Package
To install a global package, the command template for npm is:
$ npm install -g package_name[@version_number]
For example:
$ 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
Upgrade NPM
To update NPM:
$ npm install -g npm@latest changed 13 packages in 4s