Skip to content
John Mark edited this page Sep 16, 2025 · 25 revisions

Node Js

  • Node Js is non-blocking
  • Node Js runs on Single Threaded Loop
  • Off loads heavy work (file system, database etc) to background thread
  • Always use asynchronous (Promise and async-await) to maximize performance

Learning Resource

Real World Example

Useful Packages

Mutler - File Upload using Stream can handle Large File

jsonwebtoken - JWT

dotenv - env

joi - Validate inputs

pm2 - Process manager

cluster

express-rate-limit - Routing request limiter

express-mongo-sanitizer - Sanitize input

Complete Uninstall Node Js

$which node
/opt/homebrew/opt/node@20/bin/node
$brew uninstall node@20

#Remove the configuration in .zshrc
$vi ~/.zshrc

export PATH="/opt/homebrew/opt/node@20/bin:$PATH"
export LDFLAGS="-L/opt/homebrew/opt/node@20/lib"
export CPPFLAGS="-I/opt/homebrew/opt/node@20/include"
$which node
/usr/local/bin/node

$sudo rm -r /usr/local/bin/node
$sudo rm -r /usr/local/include/node
$sudo rm -r /usr/local/lib/node_modules

$sudo rm -r ~/.npm/
$sudo rm -r ~/.node_repl_history
$sudo rm -r /usr/local/bin/npm
$sudo rm -r /usr/local/share/man/man1/node.1
$sudo rm -r /usr/local/lib/dtrace/node.d

Download Multiple Node Js Versions

#Install NVM using brew
$brew install nvm

#Create nvm directory in HOME
$mkdir ~/.nvm
#Add the configs below in ~/.zshrc
export NVM_DIR="$HOME/.nvm"
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"  # This loads nvm
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"  # This loads nvm bash_completion
#Then run the command below
$source ~/.zshrc

Node Version Manager Commands

#Install current LTS Node Version
$nvm install --lts

#Check installed node version
$node --version

#Install specific node version 
$nvm install 14

#Use specific node version
$nvm use 16

#Uninstall specific node version
$nvm uninstall 14

#List all node version
$nvm ls

#Set the latest node version as the default
$nvm alias default node

#After install the latest Version Re-install any global package
$nvm install node --reinstall-packages-from=node

Generate Executable Node Js

As of this day Pkg support below >= node18 for compilation of single executable file.

#Install pkg
$npm install -g pkg

#Generate single executable file
$pkg index.js --targets node18-macos-arm64 -o test_execute

#Run the executable file
./test_execute

Best alternative is Bun and Deno

#Install bun
$npm install -g bun

$bun build index.js --compile --outfile test_file

#Run the executable file
./test_file

Run executable file using PM2

Running executable file using PM2 support any executable files. You can create executable file in Go and Nodejs tha can be run using PM2.

Install PM2 as local dependency

$npm install pm2 --dev

Add the executable file in PM2

./node_modules/pm2/bin/pm2 start -f --watch --interpreter none ./test_file

Check PM2 list

./node_modules/pm2/bin/pm2 list

Performance

  • Blocking codes halt the loop -> affect performance

Always use asynchronous API and use Promise or async/await to avoid callback hell

// Blocking
const data = fs.readFileSync('file.txt');
console.log(data);
// Non-blocking
const fs = require('fs/promises');
async function readFile() {
  const data = await fs.readFile('file.txt', 'utf8');
  console.log(data);
}

Dont block event loop

// Blocks event loop for 5 seconds!
function blockCPU() {
  const start = Date.now();
  while (Date.now() - start < 5000) {}
}
//Good (Offload to Worker Thread)
const { Worker } = require('worker_threads');

function runHeavyTask() {
  return new Promise((resolve, reject) => {
    const worker = new Worker('./heavyTask.js'); //CPU Intensive task should be in the worker
    worker.on('message', resolve);
    worker.on('error', reject);
  });
}

Caching

//In memory, fastest and good for light data
const cache = new Map();

function getUser(id) {
  if (cache.has(id)) {
     return cache.get(id);
  }
  const user = fetchUserFromDB(id);
  cache.set(id, user);
  return user;
}
//Redis (persistent and distributed)
const redis = require('redis');
const client = redis.createClient();

client.get('user:123', (err, reply) => {
  if (reply) return JSON.parse(reply);
  // Fetch from DB and set
});

Clustering and Worker threads

  • Use cluster module to scale across CPU
  • One instance per CPU more concurrency
  • Use worker_threads for CPU intensive tasks
const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  os.cpus().forEach(() => cluster.fork());
} else {
  require('./app');
}

Monitor and Profile

  • Use prom-client with Grafana
const client = require('prom-client');
const httpRequestDurationMicroseconds = new client.Histogram({
  name: 'http_request_duration_ms',
  help: 'Duration of HTTP requests in ms',
  labelNames: ['method', 'route', 'code'],
});

Run on multiple CPU using Cluster

const cluster = require('cluster');
const http = require('http');
const totalCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
  for (let i = 0; i < totalCPUs; i++) {
    cluster.fork();
  }
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Server Response: Success');
  }).listen(8000);
}

Run on multiple CPU using PM2

$pm install -g pm2
$pm2 start app.js -i max  # Runs app on all available CPU cores

PM2 Sample Setup

Security

sql

  • use parameter in sql to prevent sql injection
const query = 'SELECT * FROM users WHERE email = ?';
connection.query(query, [req.body.email]);

JWT or PASETO

const jwt = require('jsonwebtoken');

const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
  expiresIn: '1h'
});
const decoded = jwt.verify(token, process.env.JWT_SECRET);

helmet

  • protect from xss, clickjacking and other header settings.
const helmet = require('helmet');
app.use(helmet());

.env

require('dotenv').config();
const dbPassword = process.env.DB_PASSWORD;

valdate input

const Joi = require('joi');

const schema = Joi.object({
  username: Joi.string().alphanum().min(3).max(30).required(),
  password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{6,30}$')).required()
});
const { error } = schema.validate(req.body);
if (error) {
  return res.status(400).send(error.details[0].message);
}

rate limiter

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);

use https

app.use((req, res, next) => {
  if (req.headers['x-forwarded-proto'] !== 'https') {
    return res.redirect('https://' + req.headers.host + req.url);
  }
  next();
});

sanitize data before input or clean xss

const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

app.use(mongoSanitize());
app.use(xss());

Node Js Security