| name | nodejs-development |
| description | Comprehensive Node.js development skill covering event loop, async patterns, streams, file system, HTTP servers, process management, and modern Node.js best practices |
| category | backend |
| tags | ["nodejs","javascript","backend","async","streams","http","api","server"] |
| version | 1.0.0 |
Node.js Development Skill
This skill provides comprehensive guidance for building modern Node.js applications covering the event loop, async patterns, streams, file system operations, HTTP servers, process management, and best practices for production-ready Node.js development.
When to Use This Skill
Use this skill when:
- Building RESTful APIs and backend services
- Creating command-line interface (CLI) tools and utilities
- Developing microservices and distributed systems
- Building real-time applications with WebSockets or Server-Sent Events
- Creating build tools, task runners, and development tools
- Working with file processing and data transformation
- Implementing server-side data validation and business logic
- Building proxy servers and middleware layers
- Creating automation scripts and system utilities
- Developing serverless functions and cloud-native applications
- Implementing background job processors and workers
- Building GraphQL servers and API gateways
Core Concepts
Event Loop
The event loop is the heart of Node.js, enabling non-blocking I/O operations despite JavaScript being single-threaded.
Event Loop Phases:
console.log('1 - Start');
setTimeout(() => {
console.log('2 - Timeout');
}, 0);
setImmediate(() => {
console.log('3 - Immediate');
});
Promise.resolve().then(() => {
console.log('4 - Promise');
});
process.nextTick(() => {
console.log('5 - Next Tick');
});
console.log('6 - End');
Event Loop Best Practices:
const data = fs.readFileSync('large-file.txt');
fs.readFile('large-file.txt', (err, data) => {
});
const data = await fs.promises.readFile('large-file.txt');
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const { Worker } = require('worker_threads');
function computeInWorker(n) {
return new Promise((resolve, reject) => {
const worker = new Worker('./fibonacci-worker.js', {
workerData: n
});
worker.on('message', resolve);
worker.on('error', reject);
});
}
Modules
Node.js supports both CommonJS and ES Modules for organizing code.
CommonJS (Traditional):
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = { add, subtract };
exports.multiply = (a, b) => a * b;
const { add, subtract } = require('./math');
const fs = require('fs');
const express = require('express');
console.log(add(5, 3));
ES Modules (Modern):
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export default class Calculator {
add(a, b) { return a + b; }
}
import { add, subtract } from './math.mjs';
import Calculator from './math.mjs';
import fs from 'fs';
import express from 'express';
console.log(add(5, 3));
Built-in Modules:
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const crypto = require('crypto');
const os = require('os');
const events = require('events');
const stream = require('stream');
const util = require('util');
const child_process = require('child_process');
const cluster = require('cluster');
const url = require('url');
const querystring = require('querystring');
Async Programming
Node.js provides multiple patterns for handling asynchronous operations.
Callbacks (Traditional):
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log('File contents:', data);
});
fs.readFile('file1.txt', (err, data1) => {
if (err) return console.error(err);
fs.readFile('file2.txt', (err, data2) => {
if (err) return console.error(err);
fs.readFile('file3.txt', (err, data3) => {
if (err) return console.error(err);
console.log(data1, data2, data3);
});
});
});
Promises:
const fs = require('fs').promises;
fs.readFile('file.txt', 'utf8')
.then(data => {
console.log('File contents:', data);
return data;
})
.catch(err => {
console.error('Error:', err);
})
.finally(() => {
console.log('Operation complete');
});
fs.readFile('file1.txt', 'utf8')
.then(data1 => {
console.log('File 1:', data1);
return fs.readFile('file2.txt', 'utf8');
})
.then(data2 => {
console.log('File 2:', data2);
return fs.readFile('file3.txt', 'utf8');
})
.then(data3 => {
console.log('File 3:', data3);
})
.catch(err => {
console.error('Error:', err);
});
Promise.all([
fs.readFile('file1.txt', 'utf8'),
fs.readFile('file2.txt', 'utf8'),
fs.readFile('file3.txt', 'utf8')
])
.then(([data1, data2, data3]) => {
console.log(data1, data2, data3);
})
.catch(err => {
console.error('Error:', err);
});
Promise.race([
fetch('https://api1.com'),
fetch('https://api2.com')
]);
Promise.allSettled([
promise1,
promise2,
promise3
]);
Promise.any([
promise1,
promise2
]);
Async/Await (Modern):
const fs = require('fs').promises;
async function readFile() {
try {
const data = await fs.readFile('file.txt', 'utf8');
console.log('File contents:', data);
return data;
} catch (err) {
console.error('Error:', err);
throw err;
}
}
async function readFilesSequentially() {
try {
const data1 = await fs.readFile('file1.txt', 'utf8');
const data2 = await fs.readFile('file2.txt', 'utf8');
const data3 = await fs.readFile('file3.txt', 'utf8');
return [data1, data2, data3];
} catch (err) {
console.error('Error:', err);
throw err;
}
}
async function readFilesParallel() {
try {
const [data1, data2, data3] = await Promise.all([
fs.readFile('file1.txt', 'utf8'),
fs.readFile('file2.txt', 'utf8'),
fs.readFile('file3.txt', 'utf8')
]);
return [data1, data2, data3];
} catch (err) {
console.error('Error:', err);
throw err;
}
}
const data = await fs.readFile('config.json', 'utf8');
const config = JSON.parse(data);
async function robustOperation() {
try {
const result = await riskyOperation();
return { success: true, data: result };
} catch (err) {
console.error('Operation failed:', err);
return { success: false, error: err.message };
}
}
async function multipleOperations() {
const results = await Promise.allSettled([
operation1(),
operation2(),
operation3()
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Operation ${index} succeeded:`, result.value);
} else {
console.error(`Operation ${index} failed:`, result.reason);
}
});
}
Promisify Utility:
const util = require('util');
const fs = require('fs');
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
async function processFile() {
const data = await readFile('input.txt', 'utf8');
const processed = data.toUpperCase();
await writeFile('output.txt', processed);
}
function promisify(fn) {
return (...args) => {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
};
}
Streams
Streams are one of Node.js's most powerful features for handling data efficiently.
Stream Types:
const fs = require('fs');
const { Readable, Writable, Duplex, Transform } = require('stream');
const readStream = fs.createReadStream('large-file.txt', {
encoding: 'utf8',
highWaterMark: 64 * 1024
});
readStream.on('data', (chunk) => {
console.log('Received chunk:', chunk.length);
});
readStream.on('end', () => {
console.log('Finished reading');
});
readStream.on('error', (err) => {
console.error('Error:', err);
});
const writeStream = fs.createWriteStream('output.txt');
writeStream.write('Hello ');
writeStream.write('World\n');
writeStream.end();
writeStream.on('finish', () => {
console.log('Finished writing');
});
fs.createReadStream('input.txt')
.pipe(fs.createWriteStream('output.txt'));
class UpperCaseTransform extends Transform {
_transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
}
fs.createReadStream('input.txt')
.pipe(new UpperCaseTransform())
.pipe(fs.createWriteStream('output.txt'));
class NumberStream extends Readable {
constructor(max) {
super();
this.current = 0;
this.max = max;
}
_read() {
if (this.current <= this.max) {
this.push(String(this.current++));
} else {
this.push(null);
}
}
}
const numberStream = new NumberStream(10);
numberStream.on('data', (num) => {
console.log(num);
});
class LogStream extends Writable {
_write(chunk, encoding, callback) {
console.log(`[LOG] ${chunk.toString()}`);
callback();
}
}
const logStream = new LogStream();
logStream.write('Message 1\n');
logStream.write('Message 2\n');
Stream Patterns:
const { pipeline } = require('stream');
const { createReadStream, createWriteStream } = require('fs');
const { createGzip } = require('zlib');
pipeline(
createReadStream('input.txt'),
createGzip(),
createWriteStream('input.txt.gz'),
(err) => {
if (err) {
console.error('Pipeline failed:', err);
} else {
console.log('Pipeline succeeded');
}
}
);
const { finished, pipeline: promisePipeline } = require('stream/promises');
async function processFile() {
await promisePipeline(
createReadStream('input.txt'),
createGzip(),
createWriteStream('output.txt.gz')
);
console.log('Processing complete');
}
const reader = createReadStream('large-file.txt');
const writer = createWriteStream('output.txt');
reader.on('data', (chunk) => {
const canContinue = writer.write(chunk);
if (!canContinue) {
reader.pause();
}
});
writer.on('drain', () => {
reader.resume();
});
File System
The fs module provides file system operations.
Reading Files:
const fs = require('fs');
const fsPromises = require('fs').promises;
try {
const data = fs.readFileSync('file.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log(data);
});
fsPromises.readFile('file.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
async function readFile() {
try {
const data = await fsPromises.readFile('file.txt', 'utf8');
console.log(data);
} catch (err) {
console.error('Error:', err);
}
}
async function readJSON(filename) {
const data = await fsPromises.readFile(filename, 'utf8');
return JSON.parse(data);
}
Writing Files:
await fsPromises.writeFile('output.txt', 'Hello World');
await fsPromises.appendFile('log.txt', 'New log entry\n');
async function writeJSON(filename, data) {
await fsPromises.writeFile(
filename,
JSON.stringify(data, null, 2)
);
}
async function atomicWrite(filename, data) {
const tempFile = `${filename}.tmp`;
await fsPromises.writeFile(tempFile, data);
await fsPromises.rename(tempFile, filename);
}
File Operations:
const path = require('path');
async function fileExists(filename) {
try {
await fsPromises.access(filename);
return true;
} catch {
return false;
}
}
const stats = await fsPromises.stat('file.txt');
console.log({
size: stats.size,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
modified: stats.mtime,
created: stats.birthtime
});
await fsPromises.copyFile('source.txt', 'dest.txt');
await fsPromises.rename('old-name.txt', 'new-name.txt');
await fsPromises.unlink('file-to-delete.txt');
await fsPromises.mkdir('new-directory', { recursive: true });
const files = await fsPromises.readdir('directory');
console.log(files);
const entries = await fsPromises.readdir('directory', {
withFileTypes: true
});
for (const entry of entries) {
if (entry.isFile()) {
console.log('File:', entry.name);
} else if (entry.isDirectory()) {
console.log('Directory:', entry.name);
}
}
await fsPromises.rmdir('directory');
await fsPromises.rm('directory', { recursive: true, force: true });
const watcher = fs.watch('file.txt', (eventType, filename) => {
console.log(`File ${filename} changed: ${eventType}`);
});
watcher.close();
Path Utilities:
const path = require('path');
const fullPath = path.join('/users', 'john', 'documents', 'file.txt');
const absolute = path.resolve('documents', 'file.txt');
path.dirname('/users/john/file.txt');
path.basename('/users/john/file.txt');
path.basename('/users/john/file.txt', '.txt');
path.extname('file.txt');
const parsed = path.parse('/users/john/file.txt');
const formatted = path.format({
dir: '/users/john',
base: 'file.txt'
});
path.normalize('/users/john/../jane/./file.txt');
path.isAbsolute('/users/john');
path.isAbsolute('documents/file.txt');
path.relative('/users/john', '/users/jane/file.txt');
HTTP/HTTPS
Creating HTTP servers and making HTTP requests.
HTTP Server:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
const server = http.createServer((req, res) => {
const { method, url } = req;
if (url === '/' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Home Page</h1>');
} else if (url === '/api/users' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users: ['Alice', 'Bob'] }));
} else if (url === '/api/users' && method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
const data = JSON.parse(body);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, data }));
});
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
server.listen(3000);
const server = http.createServer(async (req, res) => {
try {
if (req.url === '/api/data' && req.method === 'GET') {
const data = { message: 'Hello', timestamp: Date.now() };
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(data));
} else if (req.url === '/api/data' && req.method === 'POST') {
const body = await getRequestBody(req);
const data = JSON.parse(body);
const result = { received: data, processed: true };
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'Not found' }));
}
} catch (err) {
res.writeHead(500);
res.end(JSON.stringify({ error: err.message }));
}
});
function getRequestBody(req) {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => resolve(body));
req.on('error', reject);
});
}
HTTPS Server:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Secure Hello World\n');
});
server.listen(443);
Making HTTP Requests:
const https = require('https');
https.get('https://api.example.com/data', (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(JSON.parse(data));
});
}).on('error', (err) => {
console.error('Error:', err.message);
});
const postData = JSON.stringify({ name: 'John', age: 30 });
const options = {
hostname: 'api.example.com',
port: 443,
path: '/users',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Response:', JSON.parse(data));
});
});
req.on('error', (err) => {
console.error('Error:', err.message);
});
req.write(postData);
req.end();
function httpRequest(url, options = {}) {
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve({
statusCode: res.statusCode,
headers: res.headers,
body: data
});
});
});
req.on('error', reject);
if (options.body) {
req.write(options.body);
}
req.end();
});
}
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}
async function postData() {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'John', age: 30 })
});
const result = await response.json();
console.log(result);
}
Process and Environment
Managing process lifecycle and environment variables.
Process Object:
console.log('Node version:', process.version);
console.log('Platform:', process.platform);
console.log('Architecture:', process.arch);
console.log('Process ID:', process.pid);
console.log('Current directory:', process.cwd());
console.log('Memory usage:', process.memoryUsage());
console.log('CPU usage:', process.cpuUsage());
console.log('Uptime:', process.uptime());
console.log('Arguments:', process.argv);
function parseArgs() {
const args = {};
for (let i = 2; i < process.argv.length; i += 2) {
const key = process.argv[i].replace('--', '');
const value = process.argv[i + 1];
args[key] = value;
}
return args;
}
const config = parseArgs();
console.log(config);
const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || 'development';
const dbUrl = process.env.DATABASE_URL;
console.log('Port:', port);
console.log('Environment:', nodeEnv);
process.env.MY_VAR = 'value';
process.exit(0);
process.exit(1);
const EXIT_CODES = {
SUCCESS: 0,
GENERAL_ERROR: 1,
INVALID_ARGUMENT: 2,
CONFIG_ERROR: 3
};
if (!config.isValid) {
console.error('Invalid configuration');
process.exit(EXIT_CODES.CONFIG_ERROR);
}
process.on('exit', (code) => {
console.log(`Process exiting with code ${code}`);
});
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled rejection:', reason);
process.exit(1);
});
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
server.close(() => {
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully');
process.exit(0);
});
process.kill(process.pid, 'SIGTERM');
Child Processes:
const { exec, execFile, spawn, fork } = require('child_process');
exec('ls -la', (err, stdout, stderr) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('Output:', stdout);
console.error('Errors:', stderr);
});
execFile('node', ['--version'], (err, stdout, stderr) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('Node version:', stdout);
});
const ls = spawn('ls', ['-la', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`Process exited with code ${code}`);
});
const child = fork('./child.js');
child.on('message', (msg) => {
console.log('Message from child:', msg);
});
child.send({ hello: 'child' });
process.on('message', (msg) => {
console.log('Message from parent:', msg);
});
process.send({ hello: 'parent' });
const util = require('util');
const execPromise = util.promisify(exec);
async function runCommand() {
try {
const { stdout, stderr } = await execPromise('ls -la');
console.log('Output:', stdout);
} catch (err) {
console.error('Error:', err);
}
}
Cluster Module:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master process ${process.pid} is running`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Process ${process.pid} handled request\n`);
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
API Reference
Buffer
Buffers handle binary data in Node.js.
const buf1 = Buffer.from('Hello World');
const buf2 = Buffer.from([72, 101, 108, 108, 111]);
const buf3 = Buffer.alloc(10);
const buf4 = Buffer.allocUnsafe(10);
buf3.write('Hello');
console.log(buf3.toString());
console.log(buf3.toString('hex'));
const buf5 = Buffer.concat([buf1, buf2]);
const buf6 = buf1.slice(0, 5);
Buffer.compare(buf1, buf2);
const json = JSON.stringify(buf1);
const parsed = Buffer.from(JSON.parse(json));
Events
EventEmitter is the foundation of Node.js's event-driven architecture.
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (arg1, arg2) => {
console.log('Event occurred:', arg1, arg2);
});
myEmitter.emit('event', 'arg1', 'arg2');
myEmitter.once('oneTime', () => {
console.log('This will only fire once');
});
function handler() {
console.log('Handler called');
}
myEmitter.on('event', handler);
myEmitter.off('event', handler);
myEmitter.on('error', (err) => {
console.error('Error occurred:', err);
});
class Logger extends EventEmitter {
log(message) {
this.emit('log', { message, timestamp: Date.now() });
}
error(message) {
this.emit('error', { message, timestamp: Date.now() });
}
}
const logger = new Logger();
logger.on('log', (data) => {
console.log(`[${new Date(data.timestamp).toISOString()}] ${data.message}`);
});
logger.on('error', (data) => {
console.error(`[${new Date(data.timestamp).toISOString()}] ERROR: ${data.message}`);
});
logger.log('Application started');
logger.error('Something went wrong');
Crypto
Cryptographic operations for security.
const crypto = require('crypto');
const randomBytes = crypto.randomBytes(16).toString('hex');
console.log('Random:', randomBytes);
const hash = crypto.createHash('sha256');
hash.update('password123');
console.log('Hash:', hash.digest('hex'));
const hmac = crypto.createHmac('sha256', 'secret-key');
hmac.update('message');
console.log('HMAC:', hmac.digest('hex'));
function hashPassword(password) {
const salt = crypto.randomBytes(16).toString('hex');
const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
return { salt, hash };
}
function verifyPassword(password, salt, hash) {
const hashToVerify = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
return hash === hashToVerify;
}
const { salt, hash } = hashPassword('mypassword');
console.log(verifyPassword('mypassword', salt, hash));
function encrypt(text, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
function decrypt(text, key) {
const parts = text.split(':');
const iv = Buffer.from(parts[0], 'hex');
const encrypted = Buffer.from(parts[1], 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
let decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
const key = crypto.randomBytes(32);
const encrypted = encrypt('Hello World', key);
const decrypted = decrypt(encrypted, key);
console.log(decrypted);
Workflow Patterns
REST API Server with Express
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
app.get('/api/users', async (req, res) => {
try {
const users = await db.getUsers();
res.json(users);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.get('/api/users/:id', async (req, res) => {
try {
const user = await db.getUserById(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.post('/api/users', async (req, res) => {
try {
const user = await db.createUser(req.body);
res.status(201).json(user);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.put('/api/users/:id', async (req, res) => {
try {
const user = await db.updateUser(req.params.id, req.body);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.delete('/api/users/:id', async (req, res) => {
try {
await db.deleteUser(req.params.id);
res.status(204).end();
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal server error' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Database Integration
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true
});
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: Number,
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
async function createUser(data) {
const user = new User(data);
await user.save();
return user;
}
async function getUsers() {
return await User.find();
}
async function getUserById(id) {
return await User.findById(id);
}
async function updateUser(id, data) {
return await User.findByIdAndUpdate(id, data, { new: true });
}
async function deleteUser(id) {
return await User.findByIdAndDelete(id);
}
const { Pool } = require('pg');
const pool = new Pool({
user: 'dbuser',
host: 'localhost',
database: 'mydb',
password: 'password',
port: 5432
});
async function queryUsers() {
const result = await pool.query('SELECT * FROM users');
return result.rows;
}
async function createUser(name, email) {
const result = await pool.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
[name, email]
);
return result.rows[0];
}
Authentication & Authorization
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
async function hashPassword(password) {
const salt = await bcrypt.genSalt(10);
return await bcrypt.hash(password, salt);
}
async function verifyPassword(password, hash) {
return await bcrypt.compare(password, hash);
}
function generateToken(user) {
return jwt.sign(
{ id: user.id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
}
function verifyToken(token) {
try {
return jwt.verify(token, process.env.JWT_SECRET);
} catch (err) {
return null;
}
}
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({ error: 'Invalid token' });
}
req.user = decoded;
next();
}
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await verifyPassword(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = generateToken(user);
res.json({ token, user: { id: user.id, email: user.email } });
});
app.get('/api/profile', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
Best Practices
Error Handling
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
app.get('/api/users', asyncHandler(async (req, res) => {
const users = await db.getUsers();
res.json(users);
}));
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(message = 'Resource not found') {
super(message, 404);
}
}
class ValidationError extends AppError {
constructor(message = 'Validation failed') {
super(message, 400);
}
}
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
const message = err.isOperational ? err.message : 'Internal server error';
console.error(err);
res.status(statusCode).json({
error: message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
});
process.on('uncaughtException', (err) => {
console.error('UNCAUGHT EXCEPTION!', err);
process.exit(1);
});
process.on('unhandledRejection', (err) => {
console.error('UNHANDLED REJECTION!', err);
process.exit(1);
});
Security
const helmet = require('helmet');
app.use(helmet());
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
});
app.use('/api/', limiter);
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
credentials: true
}));
const { body, validationResult } = require('express-validator');
app.post('/api/users',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('name').trim().notEmpty(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
}
);
function sanitize(input) {
if (typeof input === 'string') {
return input.replace(/[<>]/g, '');
}
return input;
}
require('dotenv').config();
const secrets = {
jwtSecret: process.env.JWT_SECRET,
dbPassword: process.env.DB_PASSWORD,
apiKey: process.env.API_KEY
};
const requiredEnvVars = ['JWT_SECRET', 'DB_PASSWORD'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
Performance
const compression = require('compression');
app.use(compression());
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 });
app.get('/api/data', async (req, res) => {
const cachedData = cache.get('data');
if (cachedData) {
return res.json(cachedData);
}
const data = await fetchData();
cache.set('data', data);
res.json(data);
});
const pool = new Pool({
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
});
userSchema.index({ email: 1 });
app.get('/api/users', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
const users = await User.find()
.skip(skip)
.limit(limit);
const total = await User.countDocuments();
res.json({
data: users,
page,
limit,
total,
pages: Math.ceil(total / limit)
});
});
const users = await User.find();
for (const user of users) {
user.posts = await Post.find({ userId: user.id });
}
const users = await User.find().populate('posts');
app.get('/api/export', (req, res) => {
res.setHeader('Content-Type', 'application/json');
res.write('[');
let first = true;
User.find().cursor().on('data', (user) => {
if (!first) res.write(',');
res.write(JSON.stringify(user));
first = false;
}).on('end', () => {
res.write(']');
res.end();
});
});
Testing
const request = require('supertest');
const app = require('./app');
describe('GET /api/users', () => {
it('should return all users', async () => {
const res = await request(app)
.get('/api/users')
.expect(200);
expect(Array.isArray(res.body)).toBe(true);
});
});
describe('POST /api/users', () => {
it('should create a new user', async () => {
const userData = {
name: 'John Doe',
email: 'john@example.com'
};
const res = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(res.body.name).toBe(userData.name);
expect(res.body.email).toBe(userData.email);
});
it('should return 400 for invalid data', async () => {
const res = await request(app)
.post('/api/users')
.send({})
.expect(400);
});
});
jest.mock('./database');
const db = require('./database');
test('getUser returns user data', async () => {
db.getUserById.mockResolvedValue({
id: 1,
name: 'John Doe'
});
const user = await getUser(1);
expect(user.name).toBe('John Doe');
});
beforeAll(async () => {
await mongoose.connect(process.env.TEST_DB_URL);
});
afterAll(async () => {
await mongoose.connection.close();
});
beforeEach(async () => {
await User.deleteMany({});
});
Logging
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
logger.info('Server started');
logger.error('Error occurred', { error: err.message });
const morgan = require('morgan');
app.use(morgan('combined', {
stream: {
write: (message) => logger.info(message.trim())
}
}));
Deployment
const server = app.listen(PORT);
process.on('SIGTERM', () => {
console.log('SIGTERM received, closing server gracefully');
server.close(() => {
console.log('Server closed');
mongoose.connection.close(false, () => {
console.log('MongoDB connection closed');
process.exit(0);
});
});
setTimeout(() => {
console.error('Forced shutdown');
process.exit(1);
}, 30000);
});
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
const v8 = require('v8');
const os = require('os');
app.get('/metrics', (req, res) => {
const heapStats = v8.getHeapStatistics();
res.json({
memory: {
used: process.memoryUsage().heapUsed,
total: heapStats.total_heap_size,
limit: heapStats.heap_size_limit
},
cpu: process.cpuUsage(),
uptime: process.uptime(),
system: {
freemem: os.freemem(),
totalmem: os.totalmem(),
loadavg: os.loadavg()
}
});
});
Summary
This Node.js development skill covers:
- Core Concepts: Event loop, modules (CommonJS/ES), async patterns, streams, file system, HTTP/HTTPS
- API Reference: Buffer, Events, Crypto, and core modules
- Workflow Patterns: REST APIs, database integration, authentication
- Best Practices: Error handling, security, performance, testing, logging, deployment
- Real-world Examples: Complete patterns for building production-ready applications
The patterns and examples represent modern Node.js development practices for building scalable, secure, and maintainable backend applications.