Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

To update or switch versions, run webi node@<tag>.
(you can use @lts for long-term support, @beta for pre-releases, or @x.y.z for a specific version)

Files

These are the files / directories that are created and/or modified with this install:

~/.config/envman/PATH.env
~/.local/opt/node/
~/.npmrc

Cheat Sheet

Node is great for simple, snappy HTTP(S) servers, and for stitching APIs together with minimal fuss or muss.

Installing node via webi will:

  • pick a compatible version from the Node Releases API
  • download and unpack to $HOME/.local/opt/node/
  • update your PATH in $HOME/.config/envman/PATH.env
  • absolutely leave system file permissions alone
    • (no dreaded sudo npm permission errors)

Hello World

node -e 'console.log("Hello, World!")'
> Hello, World!

How to Lint and Fmt Node Code

Just by installing these alone, most code editors (vim, VS Code, etc) can automatically use them for JavaScript:

npm install --location=global fixjson@1 jshint@2 prettier@3

To run them manually on your code;

  • prettier (fmt)
    touch .prettierrc.json .prettierignore
    prettier -w '**/*.{md,js,jsx,html}'
    
  • jshintrc (lint)
    touch .jshintrc .jshintignore
    jhint -c ./.jshintrc *.js */*.js
    
  • fixjson
    (turns JavaScript Objects with comments, trailing commas, etc into actual json)
    fixjson -i 2 -w ./package.json
    

To run with GitHub Actions on PRs see "Fmt & Lint Automatically" below.

A Simple Web Server

server.js:

var http = require('http');
var app = function (req, res) {
  res.end('Hello, World!');
};
http.createServer(app).listen(8080, function () {
  console.info('Listening on', this.address());
});
node server.js

Generate a Secure Random Key

This generates a hex-encoded 128-bit random key.

node -p 'crypto.randomBytes(16).toString("hex")'

This generates a url-safe base64 256-bit random key.

node -p 'crypto.randomBytes(32).toString("base64")
            .replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")'

An Express App

mkdir my-server
pushd my-server/
npm init
npm install --save express

app.js:

'use strict';

var express = require('express');
var app = express();

app.use('/', function (req, res, next) {
  res.end('Hello, World!');
});

module.exports = app;

server.js:

'use strict';

var http = require('http');
var app = require('./app.js');

http.createServer(app).listen(8080, function () {
  console.info('Listening on', this.address());
});
npm start

How to Reverse Proxy to Node

You can use caddy:

./Caddyfile:

localhost {
    # Reverse Proxy to your Node app's API
    handle /api/* {
        reverse_proxy localhost:3000
    }

    # Handle static files directly with Caddy
    handle /* {
        root * ./public/
        file_server
    }
}
caddy run --config ./Caddyfile

See the Caddy Cheat Sheet for more info, such as how to use X-SendFile to .

How to Allow Node to bind on 80 & 443

You can... but should you?

Are You Sure?

Typically you should use caddy as a Reverse Proxy (see above).

Yes

On macOS all programs have permissions to use privileged ports by default.

On Linux there are several ways to add network capabilities for privileged ports:

  1. Use setcap-netbind

    webi setcap-netbind
    setcap-netbind caddy
    
  2. Use setcap directly

    my_caddy_path="$( command -v caddy )"
    my_caddy_absolute="$( readlink -f "${my_caddy_path}" )"
    
    sudo setcap cap_net_bind_service=+ep "${my_caddy_absolute}"
    
  3. Add the --set-cap-net-bind option to serviceman (see below)

  4. Update the systemd config directly:

    CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
    AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
    

How to run a Node app as a User Service

Also called "Login Item", "Startup Item" or "User Unit", this is how you run a Node app as a Non-System (Unprivileged) Service on Mac, Windows, and Linux:

  1. Install serviceman

    webi serviceman
    
  2. Use Serviceman to create a Launch Agent (macOS), Startup Item (Windows), or User Unit (Linux):

    my_username="$( id -u -n )"
    
    serviceman add --user --name my-node-project -- \
        caddy run --config ./Caddyfile --envfile ~/.config/caddy/env
    
  3. Manage the service

    • On macOS

      # Manage (-w to disable/enable on login)
      launchctl unload -w ~/Library/LaunchAgents/my-node-project.plist
      launchctl load -w ~/Library/LaunchAgents/my-node-project.plist
      
      # View Logs
      tail -f ~/.local/share/my-node-project/var/log/my-node-project.log
      
    • On Windows

      # Manage
      serviceman stop caddy
      serviceman start caddy
      
      # View Logs
      type ~/.local/share/my-node-project/var/log/my-node-project.log | more
      
    • On Linux

      # Manage
      systemctl --user disable my-node-project
      systemctl --user stop my-node-project
      systemctl --user enable my-node-project
      systemctl --user start my-node-project
      
      # View Logs
      journalctl --user -xef -u my-node-project
      

How to run a Node app as a System Service

pushd ./my-node-project/

my_username="$( id -u -n )"
sudo env PATH="$PATH" \
    serviceman add --system --path "$PATH" --cap-net-bind \
    --name my-node-project --username "${my_username}" -- \
        npm run start

... with auto-reload in Dev

pushd ./my-node-project/

sudo env PATH="$PATH" \
    serviceman add --system --path "$PATH" --cap-net-bind \
    --name my-node-project --username "$(id -u -n)" -- \
        npx -p nodemon@3 -- nodemon ./server.js

View Logs & Restart

sudo journalctl -xef -u my-node-project
sudo systemctl restart my-node-project

How to Fmt & Lint Automatically

Here are some useful scripts to have in your package.json, and a sample file to run them with GitHub Actions (Workflows):

npm run
        fmt
        lint
        bump <major|minor|patch|prerelease>
        prepublish # also runs after npm install
# bump
npm pkg set scripts.bump='npm version -m "chore(release): bump to v%s"'

# fmt
npm pkg set scripts.fmt='npm run fixjson && npm run prettier'
npm pkg set scripts.prettier="npx -p prettier@2 -- prettier --write '**/*.{md,js,jsx,json,css,html,vue}'"
npm pkg set scripts.fixjson="npx -p fixjson@1 -- fixjson -i 2 -w '*.json' '*/*.json'"
echo 'node_modules' >> .prettierignore

# lint
npm pkg set scripts.lint='npm run jshint'
npm pkg set scripts.jshint="npx -p jshint@2 -- jshint -c ./.jshintrc ./*.js ./*/*.json"
echo 'node_modules' >> .jshintignore

# prepublish
npm pkg set scripts.prepublish='npm run lint && npm run fmt'

To run these automatically for all PRs on GitHub:

.github/workflows/node.js.yml:

name: Node.js CI
on:
  push:
    branches: ['main']
  pull_request:
jobs:
  build:
    name: "Fmt, Lint, & Test"
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version:
          - 20.x
          - latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: "Webi: Install 'shfmt' and 'shellcheck', and update PATH"
        run: |
          #sh ./_scripts/install-ci-deps
          echo "${HOME}/.local/bin" >> $GITHUB_PATH
      - run: node --version
      - run: npm run fmt
      - run: npm clean-install
      - run: npm run lint
      - run: npm run test

Contribute

Report an Issue Submit Installer Star on GitHub