Joshua's Cheatsheets - Node and NPM - Cheatsheet
Light
help

Upgrading NPM itself

  • Check version with npm -v
  • Upgrade depends on OS - full details

    • *nix - npm install -g npm@latest or npm install -g npm@next
    • Windows: Use this tool

      • npm install --global --production npm-windows-upgrade
      • npm-windows-upgrade --npm-version latest or npm-windows-upgrade
      • If any of the above fail:

        • Make sure you are in elevated prompt
        • Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force
        • If still failing, you might need to upgrade Node - [link]https://nodejs.org/en/download/)

Finding the NPM source directory

where npm

Upgrading Node itself version

  • On Unix

    • Recommended way is to use "N"

      # Just needed once
      npm install -g n
      # Now upgrade
      sudo n stable
  • On windows

    • You can download new installer (MSI) and just install over current
    • You can now use "NVM" (node version manager) for both Unix and Windows

Init

npm init, or without the step-by-step questions, npm init -y

Some useful install flags - see full list here

Flag Full Flag Description
-g --global Save globally instead of into local project
NA --link Link globally installed packages into local project
-P --save-prod Save into dependencies section. Default
-D --save-dev Package will show in devDependencies
-O --save-optional Package will show in optionalDependencies
NA --no-save Does not save to package.json at all!

Viewing and Exploring the Contents of an NPM Package

Aside from installing locally and then manually exploring the files added to your computer under node_modules, you can also explore the contents of a published NPM package with some online tools:

  • UNPKG

    • Sample Options:

      • unpkg.com/:package@:version/:file (specific file)
      • unpkg.com/:package@:version/ (directory listing)
    • For example: unpkg.com/react/
  • runpkg

    • Similar options to UNPKG, and you should be able to just add r at the beginning of a unpkg URL to redirect
    • Example: runpkg.com/react/

Semver

Updating release versions

You can use npm version {semVerString} to set a new version. This will update package.json, package-lock.json, and npm-shrinkwrap.json.

A helpful thing to remember is that, without any other arguments, this does a lot of automatic stuff you might not want:

  • Adds a new git commit with automated message

    • Use custom with -m arg
  • Add a new git tag, and points it at the auto commit

    • Use --no-git-tag-version to disable
  • Updates the npm files (as mentioned above)
  • Runs preversion, version and postversion under scripts.

Update version without tagging OR adding commit

Use the --no-git-tag-version arg, or git-tag-version=false

Auto version bump

If you don't want to manually type out a version string, you can just auto bump the version, so long as you are using a valid semVer in your npm files. Use npm version {incrementTarget} where {incrementTarget} is one of:

  • major
  • minor
  • patch
  • premajor
  • preminor
  • prepatch
  • prerelease

List installed packages

Courtesy of jkomyno

# NPM
npm list -g --depth 0

# Yarn
yarn global list

Find top level package that is using another

If we have package {top-level} that is using {sub-depend}, but we don't know that - all we know is that one of our top packages is using "sub-depend", we can find it out by using:

npm ls {sub-dependency-name}

List versions

node -v

and

npm -version

Note: Node also has "ABI" version number. regular version number is like 10.##.# (LTS or non-lts). ABI version is regular integer

node -p "process.versions.modules"

Or to get all version info:

node -p "process.versions"

Get the version of a package

NPM

npm list {packageName}

Yarn

yarn list --pattern {packageNameOrPattern} Or... yarn list | grep {packageNameOrPattern} Or... yarn global list --pattern {packageNameOrPattern}

Install a very specific version of a package (from CLI)

You can actually use similar syntax as a package.json file - e.g. tslint@^5.0.0.

Updating / upgrading packages used in package.json

  • NPM

    • You can use npm update to pull in updated packages (will also update your package-lock.json automatically)
    • You can use npm outdated to see what your package.json is requesting VS what is published

      • Good to use this to double-check after running npm update, since npm update requests based on your package.json semver rules, not necessarily what is newest
    • For upgrading to the absolute latest, ignoring package.json, there is no built-in command (unlike Yarn)

      • The easiest way to do it by hand, is to edit the package.json file to have * as the semver for any dependency you want to have updated to the latest, and then run npm update again.

        • Since npm knowns * is invalid, it will update and then overwrite the entry
      • Another option is to call npm install PACKAGE_NAME@latest for each dependency you want updated
      • For an automated / interactive 3rd party solution, you could check out one of these:

  • Yarn:

    • Upgrade to latest specified by package.json: yarn upgrade
    • Same as above, but interactively: yarn upgrade-interactive
    • Upgrade to absolute latest, ignoring package.json (for example, major version bump): yarn upgrade-interactive --latest
    • You can use yarn outdated to see what your package.json is requesting VS what is published

      • See notes above on npm outdated, since this works similarly

Installing a specific version

  • NPM

    • npm install {packageName}@{semVerPattern}
  • Yarn

    • yarn add {packageName}@{semVerPattern}

How to deal with nested dependency versions

A somewhat common issue is trying to force a direct dependency (listed under dependencies or devDependencies in package.json) to use a specific version of their dependency.

This is not something you normally dictate - it is normally handled automatically by NPM or Yarn - but sometimes you might want to force a certain sub-dependency version due to a vulnerability; for example, if you use my-dependency which has a nested dependency of my-sub-depend locked at v0.0.1, which has a severe vulnerability patched in v0.0.2.

There are multiple ways to approach this, and the best starting spot is this S/O Question.

  • NPM

    • Use Shrinkwrap file

      1. Run npm shrinkwrap
      2. Manually edit the auto-created npm-shrinkwrap.json file

        • Use Semver targeting to force the new version you want
      3. Run npm install again
    • Use an automated solution, like npm-force-resolutions
  • Yarn

    • Yarn is way easier to use overrides with, in comparison to NPM
    • Just add resolutions object to package.json with overrides

When in doubt and messed up beyond repair...

npm cache clean -force

When REALLY messed up

rm -rf node_modules
npm cache clean -force
npm install

Installing peer-dependencies

Many packages, especially lint configs, will require other dependencies (peer dependencies) but will not install them automatically. You could manually figure out what's needed (for example, by running npm ls) and manually install one by one, or use a tool like install-peerdeps.

# See: https://www.npmjs.com/package/install-peerdeps#usage

# NPM
npm install -g install-peerdeps

# Yarn
yarn global add install-peerdeps

install-peerdeps --dev {package}
# OR
install-peerdeps {package}
# OR
install-peerdeps <package>[@<version>]

You can list peer dependencies needed by a package by using

# NPM
npm info "{package}" peerDependencies

Using global modules / packages

You can use globally installed packages by taking advantage of link - which basically just symlinks from the global folder to your project directory. Once something is installed globally, you can simply run npm link {packageName} in the directory where you want to be able to access it.

WARNING: This is not advised! In general, packages that good to install globally and use that way are CLI tools and devops / formatting packages, not actual dependencies that could impact a project.

Currently, Yarn will allow linking of dev packages (e.g. yarn link in dev, yarn link {name} in test), but tends to have issues with globally installed versions (GH issue).

NPM Install Errors

  • "No prebuilt binaries found"

    • There is probably a dependency that was built using C++ or other codebase and is missing a prebuilt binary for your versions of node/ABI#. Check your ABI and compare against what is hosted as a prebuilt binary.

      • If you can't find a prebuilt to use, just build yourself (check dependency's package.json and .gyp for details). You will probably also need build tools installed, depending on OS:

        npm install --global --production windows-build-tools
  • General build errors ("MSBuild", etc)

    • Make sure that you have build tools installed (see above)

      • Make sure built-tools install WORKED

        • Make sure to run as Admin
        • Check log, and if necessary, manually run the installer it downloads
    • You can switch what version of MSVC you are targeting:

      • For example: npm config set msvs_version 2015 -g
    • Try to get Node to use a prebuilt-binary instead of building from scratch

      • Check the log to see why node is trying to rebuild/build

        • For example, is a firewall blocking the binary download?
        • Some install scripts will let you specify a binary source URL
      • A quick hack if you can find a pre-built binary to download, is to save it to the corresponding npm-cache folder, and then run npm rebuild {packageName} before trying to re-install.

        • You should see Cached binary found at ... if this trick worked
        • Found via this comment

Paths, paths, paths

Current directory

Often in Node scripts, you will need to reference something either by absolute or relative path, which might require knowing the full path of where the script is running.

  • __dirname is a magic global that holds the string path of where the script resides - regardless from where it got called

    • Does not have trailing slash
  • process.cwd() returns the absolute path from where you invoked the script process - e.g. where you ran your command from

Normalizing paths (POSIX vs non-POSIX, aka Windows)

There is a really great native library for Node called path which has all kinds of methods for cleaning up and parsing paths. For example, if I want to create a path based on current directory, and then normalize it because I'm not sure which OS it is going to be running on, I might use something like this:

const path = require('path');
const myFilePath = path.normalize(__dirname + '../subdir/myFile.js');

However, that only normalizes it for the OS you are on. If you want to force a standard, across any env, that takes more work. Quick hackish example:

currFilePath = currFilePath.replace(/[\/\\]{1,2}/gm,'/');

Read more about path, here

Get list of node flags

node --help

Or here.

Using the Node CLI / REPL

You can use the -e argument to evaluate (eval) raw code string, or -p to both evaluate and "print" the output.

node -p "Math.min(24,2);"

Results in "2" being printed to console.

Running a CLI command / system / bash / shell commands from within a node script

Recommended way is with child_process.exec.

const childProc = require('child_process');
var lsResults = childProc.execSync('ls');
// Note - exec returns buffer, so need to convert
var lsResultsString = lsResults.toString();

Changing directory

If you want to use cd, you need to use it with the command you want to run at the same time - e.g. a single line input to child_process.exec. Otherwise, the change of directory will not persist between commands. For example:

const childProc = require('child_process');

// Right:
childProc.execSync('cd foo && ls');

// Wrong:
chldProc.execSync('cd foo');
childProc.execSync('ls');

A recommended alternative is to pass the directory you want to execute the command in through the cwd (working directory) option:

const childProc = require('child_process');
childProc.execSync('ls', {
	cwd: 'foo'
});

Receiving CLI arguments

Access through process.argv. It should follow the following syntax:

if (Array.isArray(process.argv)){
	// 0 = path to nodejs - {string}
	let nodePath = process.argv[0];
	// 1 = path to current executing file - {string}
	let currFile = process.argv[1];
	// 2, 3, etc. = arguments
	let argsArr = process.argv.slice(2);
}

Detecting when a script / file is being run via CLI

There is a really handy trick for, in your code, to detect if it is being run directly versus imported by other code. You can use:

if (require.main === module) {
	// This code will only execute if the file is called *directly* (e.g. via CLI)
}

Also, see "Accessing the main module"

Read in package.json within script file

const packageInfo = require('./package.json');
console.log('Version = ' + packageInfo.version);

Also see "JS Modules - How to Import JSON?"

List tasks

npm run

Setting globals

There are not many good reasons to do this, but if for some reason you need to set a true global (not as in file global or top of closure global, but as in pollutes every file once imported), here is how:

global.findMyAnywhere = 'Hello';
// Or...
globalThis.findMyAnywhere = 'Hello';

Details

Console EOF / process.stdin.on('end')

On Unix, pressing CTRL+D usually results in the system returning an EOF (end-of-file) to whatever is listening to the terminal. In node, you often see this listened to as:

process.stdin.on('end', ()=>{
	// Do something
}

However, this flat out does not work on Windows. Futhermore, pressing CTRL+C, since it sends the exit command, does not give that listener a chance to execute. The workaround is to use the process signal event SIGINT listener:

// Redirect windows CTRL+C to stdin-end
process.on('SIGINT',function(){
	// Do whatever you want here - finish up stuff, etc.
	// ...
	// Emit EOF / end event
	// https://nodejs.org/api/stream.html#stream_event_end
	process.stdin.emit('end');
});

Express

Great cheatsheets

Quick Ref Table

Side Thing Description Relev type sig
Request req.params Captures named params, made explicit in route signature. {[index: string]: string | undefined}
Request req.query Captures QueryString key-pair vals. {[index: string]: string | undefined}
Request req.body Captures body payload key-pairs

Requires body parsing middleware
{[index: string]: any}

How do I...

  • Capture variable through route?

    • Use :var syntax in route, then access through injected req.params

      app.get('/users/:userId', (req, res) => {
      	const userId = req.params.userId;
      });
    • Or through actual request payload, with req.body (if using body-parser)
    • Or through query params with req.query.{queryParam}
  • Inject a variable into the route?

    • You probably want to use middleware
    • Actually very easy to write your own with just a few lines of code...
  • Capture a parameter and check if it was used in the request?

    • GET (QueryString)

      • Use req.query, which returns object of key/pair values corresponding to querystring.
      • Check for valid value through standard JS logic; e.g., if you are expecting a string for /?name={name}, maybe do !!req.query.name
    • POST (Body)

      • Basically same as GET, but use req.body, to access POST body params (assuming you switch to body with POST)
      • Requires body-parsing middleware
    • Named param in route

      • If you are capturing parameters explicitly as part of the route, you can use req.params to get the captured value
      • Example, if route is /book/:id, you can get ID through req.params.id
    • ALL

      • If you want to accept the same parameter, and allow it to be passed via any of the three methods (QueryString, Body, or Named Params), that is a little trickier
      • There used to be an API method for this, req.param({name}, {defaultValue}), but it was deprecated.
      • If you really need this, you could easily write your own wrapper method to check all three inputs and validate, and/or take a look at the original code, here.
      • Relevant S/O thread

Things to note

Param types

The built-in query string parser in Express does not infer types. For example, you might be tempted to think that if your request looks like /?name=joe&age=13, then req.query.name would be typeof string, and req.query.age would be typeof number. This is wrong. They are all inferred as strings.

In addition, since req.query is an object, if you try to query a param that was not provided, you will get back undefined, not an empty string.

All of the above is also true for req.params, but not for req.body, since if JSON is passed via request, then the types are preserved without any special parsing needed

Common Issues

  • ERR_HTTP_HEADERS_SENT

    • This indicates that you are basically trying to send a response back multiple times, or trying to set headers on a response after it has already been sent
    • A very common reason for this is forgetting to return after sending a response, so your code continues and tries to send a second response.

  • BadRequestError: request aborted

    • This is kind of a generic error, indicating that something interrupted the process of reading in the request
    • Is likely to happen with the body-parser plugin
    • I personally hit this with mismatched Content-Length headers - where this error will throw if I send a request with an explicit Content-Length header that doesn't match the body size

      • Easy to accidentally do this if you are cloning Postman requests

Debugging, profiling, etc.

Debugging

Call node --inspect-brk {nodeScriptFilePath} {args}

Make sure you either have auto-attach on in your VSCode settings, or start a debug session before running.

Profiling

For a plug-n-play solution, check out 0x.

Throwing errors

The recommended way to throw errors in Node is with the explicit error constructor.

throw new Error('Computer says "no"...');

Good read: Flavio Copes - Node Exceptions


Environment Variables

Reading values

From your CLI, the fastest way to view your environment variables is node -p process.env.

From within code that is running with NodeJS, you can easily access any environment variable by picking it off the process global variable object. It is super common to use this as a way to avoid putting credentials in code, like so:

const myApiClient = new ApiClient({
	id: process.env.API_CLIENT_ID,
	superSecretPass: process.env.API_CLIENT_PASS
});

Setting values

There are multiple ways to set env values, with varying levels of setup required.

From the CLI

Since process.env is basically just a map of your OS's environment variables, setting values for it depends on your OS and even what CLI you use:

  • BASH, or "bash-like" CLIs: {KEY}={VAL}
  • Windows CMD: SETX {KEY} "{VAL}" (or, temporary, SET instead of SETX)

    • Example: SETX API_KEY "123"&& node -p process.env.API_KEY ---> results in 123 printed to console
  • Windows PowerShell: $env:{KEY}="{VAL}"

From a .env File

Since it can get tedious setting and checking variables from the command line, most devs prefer to keep these values stored in a file, and have Node read the values out when executing. This also has the added benefit of keeping those values out of your OS variables.

However, unlike how it reads OS variables, mapping values from a file to process.env is not baked into Node, so you will need to use a dependency to add that ability. The most popular is probably dotenv.

You can read the docs for how to use it, but its pretty simple:

  • Add a .env file to the root of your project, with key pair values

    • These shoule be written as KEY=VAL
  • Run npm install dotenv - to add it as a dependency
  • Add require('dotenv').config() as early as possible in your code, which will cause dotenv to map the contents of the file to process.env

    • After this point, process.env.MY_KEY will contain the value defined in .env if you have the pair MY_KEY={something} in the file

WARNING: Be careful about sharing your .env file. If it contains "secrets" (API keys, credentials, etc.) you probably want to add it to your .gitignore, and create a example.env which contains the same keys as .env, but with empty values for a dev to fill in with their own credentials.

From within Code

From within your code that is running on Node, you can override existing values, or set new ones, simply by treating process.env as a regular object. For example:

process.env.API_KEY = 'ABC123';

Note: this only sets the value for Node's process and any child processes; this doesn't actually change your OS environment variable value after the process exits!

Markdown Source Last Updated:
Sat Jul 25 2020 22:37:57 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Tue Aug 27 2019 07:54:31 GMT+0000 (Coordinated Universal Time)
© 2020 Joshua Tzucker, Built with Gatsby