-
Notifications
You must be signed in to change notification settings - Fork 0
Home
- 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
Mutler - File Upload using Stream can handle Large File
jsonwebtoken - JWT
dotenv - env
joi - Validate inputs
pm2 - Process manager
express-rate-limit - Routing request limiter
express-mongo-sanitizer - Sanitize input
$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
#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
#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
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
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
- 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
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());