Global Node Modules without sudo

Grooviz
5 min readApr 15, 2018

--

Short post about how to better manage Node Modules Installation for your NodeJS Projects.

It will not only let you install Node Modules without using root privileges but it will also ensure the following benefits of a well managed NodeJS Project:

  • Specific Node versions for specific projects
  • Specific Node Modules versions for specific projects
  • Self-sufficient package.json, no prerequisite needed before npm install

Background issue

When installing Node Modules globally with npm, you will encounter EACCESS npm errors:

$ npm install -g whatever
[...]
npm WARN checkPermissions Missing write access to /usr/lib/node_modules/whatever/whatever
[...]
npm ERR! path /usr/lib/node_modules/whatever
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall access
npm ERR! Error: EACCES: permission denied, access '/usr/lib/node_modules/whatever'
npm ERR! { Error: EACCES: permission denied, access '/usr/lib/node_modules/whatever'
npm ERR! stack: 'Error: EACCES: permission denied, access \'/usr/lib/node_modules/whatever\'',
npm ERR! errno: -13,
npm ERR! code: 'EACCES',
npm ERR! syscall: 'access',
npm ERR! path: '/usr/lib/node_modules/@angular/cli/node_modules/whatever' }
npm ERR!
npm ERR! Please try running this command again as root/Administrator.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/user/.npm/_logs/2015-10-21T16_29_42_409Z-debug.log

Below are commonly used solutions to fix this problem.

<spoiler-alert> We use the last one. </spoiler-alert>

Solution 1 :
I, For One, Welcome Our New Superuser Overlord!

The easiest solution would probably be to surrender and run the install script as root/Administrator, as suggested:

$ sudo npm install -g whatever

This will install your global Node Modules under /usr/lib/node_modules.

It works… But do you really want to use the root privileges for every global Node Modules you will install?

No, no, no…

Solution 2:
Not So Global Node Modules…

Instead of using root privileges, you may instead move the node_modules inside your current user home, in a ~/.nvm directory:

$ mkdir ~/.nvm
$ npm config set prefix '~/.nvm'

Then, at the end of your .profile, make sure you update your PATH:

PATH="$HOME/.nvm/bin:$PATH"

Relaunch your terminal or update your system variables:

$ source ~/.profile

And continue installing global Node Modules under your current user.

Solution 3:
Using Node Version Manager

nvm, Node Version Manager, is a simple bash script to manage multiple active node.js versions.

The beautiful side effect of using nvm is that the node_modules of each versions will be stored inside the current users’s home directory as ~/.nvm/versions/node/vX.Y.Z/lib/node_modules/. We don’t need root privileges to install global Node Modules anymore and we can switch from one Node Version to another, depending on the project.

Installing (or updating) nvm is as easy as:

$ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

(check on nvm for the latest nvm version)

Now, after you restart your terminal, install NodeJS (by default, the latest version):

$ nvm install node
Downloading and installing node v9.11.1...
Downloading https://nodejs.org/dist/v9.11.1/node-v9.11.1-linux-x64.tar.xz...
############################################################################################################################################## 100,0%
Computing checksum with sha256sum
Checksums matched!
Now using node v9.11.1 (npm v5.6.0)
Creating default alias: default -> node (-> v9.11.1)

Your global Node Modules will be installed inside ~/.nvm/versions/node/vX.Y.Z/lib/node_modules.

You will find more information on how to install specific versions of NodeJS and switching from one to the other on GitHub nvm page.

Solution 4:
Let the Executor do his job

This is the solution we use!

npx, the Node Package Executor, executes command either from a local node_modules/.bin, or from a central cache, installing any packages needed in order for command to run.

Yes, it will even install the packages you need to run a command in a central cache if needed!

Example: Using Angular CLI ng command to create a new Angular Application

You do not need to install Angular CLI globally, you can use npx to run ng new before installing the @angular/cli package:

$ npx -p @angular/cli ng new my-app
create my-app/README.md (1021 bytes)
create my-app/.angular-cli.json (1241 bytes)
[...]
create my-app/src/app/app.component.spec.ts (986 bytes)
create my-app/src/app/app.component.ts (207 bytes)
npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/
npm WARN deprecated node-uuid@1.4.8: Use uuid module instead
> uws@9.14.0 install /home/user/Projects/TEMP/my-app/node_modules/uws
> node-gyp rebuild > build_log.txt 2>&1 || exit 0
> node-sass@4.7.2 install /home/user/Projects/TEMP/my-app/node_modules/node-sass
> node scripts/install.js
Cached binary found at /home/user/.npm/node-sass/4.7.2/linux-x64-59_binding.node
> uglifyjs-webpack-plugin@0.4.6 postinstall /home/user/Projects/TEMP/my-app/node_modules/webpack/node_modules/uglifyjs-webpack-plugin
> node lib/post_install.js
> node-sass@4.7.2 postinstall /home/user/Projects/TEMP/my-app/node_modules/node-sass
> node scripts/build.js
Binary found at /home/user/Projects/TEMP/my-app/node_modules/node-sass/vendor/linux-x64-59/binding.node
Testing binary
Binary is fine
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.1.3 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
added 1273 packages in 36.111s

That’s it! Our Angular application is created and all needed Node Modules have been installed locally. Let’s serve our newly created Angular WebApp!

$ ng serve
The program 'ng' is currently not installed. You can install it by typing:
sudo apt install ng-common

Ooooops… But don’t run to install ng-common globally yet!!!

You just have to use npx to run locally installed npm binaries.

$ npx ng serve

Better, you could configure the Shell Auto Fallback inside your .bashrc:

$ npx --shell-auto-fallback bash
command_not_found_handle() {
# Do not run within a pipe
if test ! -t 1; then
>&2 echo "command not found: $1"
return 127
fi
if which npx > /dev/null; then
echo "$1 not found. Trying with npx..." >&2
else
return 127
fi
if ! [[ $1 =~ @ ]]; then
npx --no-install "$@"
else
npx "$@"
fi
return $?
}
$ npx --shell-auto-fallback bash >> ~/.bashrc
$ source ~/.bashrc

Now, if the command is not found, it will try to find it within the installed npm binaries.

$ ng serve
ng not found. Trying with npx...
not found: ng
$ cd my-app
$ ng serve
ng not found. Trying with npx...
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2018-03-01T08:48:23.771Z
Hash: ac1c4647111e56cc4df3
Time: 7485ms
chunk {inline} inline.bundle.js (inline) 3.85 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 20.8 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 549 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 42.2 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 8.45 MB [initial] [rendered]
webpack: Compiled successfully.

Your Angular WebApp is now running on http://localhost:4200

The npx Fallback can also work with Node Modules that have not been installed, if you specify the version:

$ cowsay "Medium is bad"
cowsay not found. Trying with npx...
not found: cowsay
$ cowsay@latest "Medium is cool"
cowsay@latest not found. Trying with npx...
npx: installed 9 in 1.23s
________________
< Medium is cool >
----------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||

Here, I specified the version of cowsay as @latest.

And, if you check inside your current global node_modules, it only contains npm:

$ ls /home/user/.nvm/versions/node/v9.6.1/lib/node_modules/
npm

Advantages of using nvm and npx

Here are the main advantages of using nvm (Node Version Manager) and npx (Node Package Executor):

  • Specific Node versions for specific projects
  • Specific Node Modules versions for specific projects
  • Self-sufficient package.json, no prerequisite needed before runningnpm install.

References

  • nvm, the Node Version Manager
  • npx, the Node Package Executor
  • Angular CLI, Angular Command Line Interface

--

--