Skip to content

Node.js Tutorial

Learn Node.js from fundamentals to advanced concepts, including modules, asynchronous programming, APIs, and production deployment.

Overview

Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine that allows you to run JavaScript on the server side. It's designed for building scalable network applications with an event-driven, non-blocking I/O model.

Getting Started

Installation

bash
# Using Node Version Manager (recommended)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install node
nvm use node

# Direct installation
# Download from https://nodejs.org

# Using package managers
# Ubuntu/Debian
sudo apt update
sudo apt install nodejs npm

# macOS (Homebrew)
brew install node

# Verify installation
node --version
npm --version

First Node.js Application

javascript
// hello.js
console.log('Hello, Node.js!')

// Run the application
// node hello.js

Interactive REPL

bash
# Start Node.js REPL
node

# In REPL
> console.log('Hello World')
> 2 + 3
> const name = 'Node.js'
> name
> .exit  // Exit REPL

Core Concepts

Event Loop

javascript
// Event loop demonstration
console.log('Start')

setTimeout(() => {
  console.log('Timeout callback')
}, 0)

setImmediate(() => {
  console.log('Immediate callback')
})

process.nextTick(() => {
  console.log('Next tick callback')
})

console.log('End')

// Output order:
// Start
// End
// Next tick callback
// Immediate callback
// Timeout callback

Global Objects

javascript
// Global objects available in Node.js
console.log(__dirname)    // Current directory
console.log(__filename)   // Current file path
console.log(process.cwd()) // Current working directory
console.log(process.env)   // Environment variables
console.log(process.argv)  // Command line arguments

// Global functions
setTimeout(() => {}, 1000)
setInterval(() => {}, 1000)
setImmediate(() => {})
clearTimeout()
clearInterval()
clearImmediate()

Process Object

javascript
// Process information
console.log('Node.js version:', process.version)
console.log('Platform:', process.platform)
console.log('Architecture:', process.arch)
console.log('Memory usage:', process.memoryUsage())
console.log('Uptime:', process.uptime())

// Command line arguments
process.argv.forEach((arg, index) => {
  console.log(`${index}: ${arg}`)
})

// Environment variables
console.log('NODE_ENV:', process.env.NODE_ENV)
console.log('PORT:', process.env.PORT)

// Exit process
process.exit(0)  // Success
process.exit(1)  // Error

Modules System

CommonJS Modules

javascript
// math.js - Creating a module
const add = (a, b) => a + b
const subtract = (a, b) => a - b
const multiply = (a, b) => a * b

// Export methods
module.exports = {
  add,
  subtract,
  multiply
}

// Alternative export syntax
exports.divide = (a, b) => a / b

// app.js - Using the module
const math = require('./math')
const { add, subtract } = require('./math')

console.log(math.add(5, 3))      // 8
console.log(subtract(10, 4))     // 6

ES6 Modules

javascript
// math.mjs - ES6 module
export const add = (a, b) => a + b
export const subtract = (a, b) => a - b

export default function multiply(a, b) {
  return a * b
}

// app.mjs - Using ES6 modules
import multiply, { add, subtract } from './math.mjs'
import * as math from './math.mjs'

console.log(add(5, 3))
console.log(multiply(4, 2))

// package.json configuration for ES6 modules
{
  "type": "module"
}

Built-in Modules

javascript
// File System
const fs = require('fs')
const path = require('path')

// HTTP
const http = require('http')
const https = require('https')

// URL and Query String
const url = require('url')
const querystring = require('querystring')

// Crypto
const crypto = require('crypto')

// OS
const os = require('os')

// Events
const EventEmitter = require('events')

// Stream
const { Readable, Writable, Transform } = require('stream')

File System Operations

Reading Files

javascript
const fs = require('fs')
const path = require('path')

// Synchronous reading
try {
  const data = fs.readFileSync('file.txt', 'utf8')
  console.log(data)
} catch (error) {
  console.error('Error reading file:', error.message)
}

// Asynchronous reading (callback)
fs.readFile('file.txt', 'utf8', (error, data) => {
  if (error) {
    console.error('Error reading file:', error.message)
    return
  }
  console.log(data)
})

// Asynchronous reading (promises)
const fsPromises = require('fs').promises

async function readFileAsync() {
  try {
    const data = await fsPromises.readFile('file.txt', 'utf8')
    console.log(data)
  } catch (error) {
    console.error('Error reading file:', error.message)
  }
}

readFileAsync()

Writing Files

javascript
const fs = require('fs')

// Synchronous writing
try {
  fs.writeFileSync('output.txt', 'Hello, Node.js!', 'utf8')
  console.log('File written successfully')
} catch (error) {
  console.error('Error writing file:', error.message)
}

// Asynchronous writing
fs.writeFile('output.txt', 'Hello, Node.js!', 'utf8', (error) => {
  if (error) {
    console.error('Error writing file:', error.message)
    return
  }
  console.log('File written successfully')
})

// Append to file
fs.appendFile('log.txt', 'New log entry\n', (error) => {
  if (error) {
    console.error('Error appending to file:', error.message)
    return
  }
  console.log('Data appended successfully')
})

Directory Operations

javascript
const fs = require('fs')
const path = require('path')

// Create directory
fs.mkdir('new-directory', { recursive: true }, (error) => {
  if (error) {
    console.error('Error creating directory:', error.message)
    return
  }
  console.log('Directory created successfully')
})

// Read directory
fs.readdir('.', (error, files) => {
  if (error) {
    console.error('Error reading directory:', error.message)
    return
  }
  console.log('Files:', files)
})

// Get file stats
fs.stat('file.txt', (error, stats) => {
  if (error) {
    console.error('Error getting file stats:', error.message)
    return
  }
  
  console.log('Is file:', stats.isFile())
  console.log('Is directory:', stats.isDirectory())
  console.log('File size:', stats.size)
  console.log('Created:', stats.birthtime)
  console.log('Modified:', stats.mtime)
})

// Watch for file changes
fs.watchFile('file.txt', (current, previous) => {
  console.log('File changed')
  console.log('Current:', current.mtime)
  console.log('Previous:', previous.mtime)
})

HTTP Server and Client

Basic HTTP Server

javascript
const http = require('http')
const url = require('url')

const server = http.createServer((request, response) => {
  const parsedUrl = url.parse(request.url, true)
  const path = parsedUrl.pathname
  const method = request.method
  
  // Set response headers
  response.setHeader('Content-Type', 'application/json')
  response.setHeader('Access-Control-Allow-Origin', '*')
  
  // Handle different routes
  if (path === '/' && method === 'GET') {
    response.statusCode = 200
    response.end(JSON.stringify({ message: 'Hello, World!' }))
  } else if (path === '/users' && method === 'GET') {
    response.statusCode = 200
    response.end(JSON.stringify({ users: ['Alice', 'Bob', 'Charlie'] }))
  } else {
    response.statusCode = 404
    response.end(JSON.stringify({ error: 'Not Found' }))
  }
})

const PORT = process.env.PORT || 3000
server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

// Handle server errors
server.on('error', (error) => {
  console.error('Server error:', error.message)
})

Handling POST Requests

javascript
const http = require('http')

const server = http.createServer((request, response) => {
  if (request.method === 'POST') {
    let body = ''
    
    // Collect data chunks
    request.on('data', (chunk) => {
      body += chunk.toString()
    })
    
    // Process complete data
    request.on('end', () => {
      try {
        const data = JSON.parse(body)
        console.log('Received data:', data)
        
        response.statusCode = 200
        response.setHeader('Content-Type', 'application/json')
        response.end(JSON.stringify({ 
          message: 'Data received successfully',
          received: data
        }))
      } catch (error) {
        response.statusCode = 400
        response.end(JSON.stringify({ error: 'Invalid JSON' }))
      }
    })
  } else {
    response.statusCode = 405
    response.end(JSON.stringify({ error: 'Method Not Allowed' }))
  }
})

server.listen(3000, () => {
  console.log('Server running on port 3000')
})

HTTP Client

javascript
const http = require('http')
const https = require('https')

// GET request
const options = {
  hostname: 'jsonplaceholder.typicode.com',
  port: 443,
  path: '/posts/1',
  method: 'GET'
}

const request = https.request(options, (response) => {
  let data = ''
  
  response.on('data', (chunk) => {
    data += chunk
  })
  
  response.on('end', () => {
    console.log('Response:', JSON.parse(data))
  })
})

request.on('error', (error) => {
  console.error('Request error:', error.message)
})

request.end()

// POST request
const postData = JSON.stringify({
  title: 'New Post',
  body: 'This is a new post',
  userId: 1
})

const postOptions = {
  hostname: 'jsonplaceholder.typicode.com',
  port: 443,
  path: '/posts',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Content-Length': Buffer.byteLength(postData)
  }
}

const postRequest = https.request(postOptions, (response) => {
  let data = ''
  
  response.on('data', (chunk) => {
    data += chunk
  })
  
  response.on('end', () => {
    console.log('POST Response:', JSON.parse(data))
  })
})

postRequest.on('error', (error) => {
  console.error('POST Request error:', error.message)
})

postRequest.write(postData)
postRequest.end()

Asynchronous Programming

Callbacks

javascript
// Callback pattern
function fetchData(callback) {
  setTimeout(() => {
    const data = { id: 1, name: 'John Doe' }
    callback(null, data)  // null for error, data as result
  }, 1000)
}

fetchData((error, data) => {
  if (error) {
    console.error('Error:', error)
    return
  }
  console.log('Data:', data)
})

// Callback hell example
getData((error, data) => {
  if (error) {
    console.error(error)
    return
  }
  
  processData(data, (error, processedData) => {
    if (error) {
      console.error(error)
      return
    }
    
    saveData(processedData, (error, result) => {
      if (error) {
        console.error(error)
        return
      }
      
      console.log('Success:', result)
    })
  })
})

Promises

javascript
// Creating promises
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.5
      
      if (success) {
        resolve({ id: 1, name: 'John Doe' })
      } else {
        reject(new Error('Failed to fetch data'))
      }
    }, 1000)
  })
}

// Using promises
fetchData()
  .then(data => {
    console.log('Data:', data)
    return processData(data)
  })
  .then(processedData => {
    console.log('Processed:', processedData)
    return saveData(processedData)
  })
  .then(result => {
    console.log('Saved:', result)
  })
  .catch(error => {
    console.error('Error:', error.message)
  })
  .finally(() => {
    console.log('Operation completed')
  })

// Promise utilities
Promise.all([fetchData(), fetchData(), fetchData()])
  .then(results => {
    console.log('All results:', results)
  })
  .catch(error => {
    console.error('One or more promises failed:', error)
  })

Promise.race([fetchData(), fetchData()])
  .then(result => {
    console.log('First result:', result)
  })
  .catch(error => {
    console.error('First error:', error)
  })

Async/Await

javascript
// Async function
async function fetchUserData(userId) {
  try {
    const user = await fetchUser(userId)
    const posts = await fetchUserPosts(userId)
    const comments = await fetchUserComments(userId)
    
    return {
      user,
      posts,
      comments
    }
  } catch (error) {
    console.error('Error fetching user data:', error.message)
    throw error
  }
}

// Using async/await
async function main() {
  try {
    const userData = await fetchUserData(1)
    console.log('User data:', userData)
  } catch (error) {
    console.error('Failed to get user data:', error.message)
  }
}

main()

// Parallel execution with async/await
async function fetchAllData() {
  try {
    const [users, posts, comments] = await Promise.all([
      fetchUsers(),
      fetchPosts(),
      fetchComments()
    ])
    
    return { users, posts, comments }
  } catch (error) {
    console.error('Error fetching data:', error.message)
    throw error
  }
}

Events and EventEmitter

Basic EventEmitter

javascript
const EventEmitter = require('events')

class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter()

// Register event listeners
myEmitter.on('event', (data) => {
  console.log('Event received:', data)
})

myEmitter.on('error', (error) => {
  console.error('Error occurred:', error.message)
})

// Emit events
myEmitter.emit('event', { message: 'Hello, Events!' })
myEmitter.emit('error', new Error('Something went wrong'))

// One-time listener
myEmitter.once('startup', () => {
  console.log('Application started')
})

myEmitter.emit('startup')  // Will trigger
myEmitter.emit('startup')  // Will not trigger

// Remove listeners
const listener = (data) => console.log('Listener:', data)
myEmitter.on('test', listener)
myEmitter.removeListener('test', listener)
myEmitter.removeAllListeners('test')

Custom EventEmitter

javascript
const EventEmitter = require('events')

class Logger extends EventEmitter {
  log(message) {
    console.log(`[${new Date().toISOString()}] ${message}`)
    this.emit('logged', { message, timestamp: new Date() })
  }
  
  error(message) {
    console.error(`[${new Date().toISOString()}] ERROR: ${message}`)
    this.emit('error', { message, timestamp: new Date() })
  }
}

const logger = new Logger()

logger.on('logged', (data) => {
  console.log('Log event:', data)
})

logger.on('error', (data) => {
  console.error('Error event:', data)
})

logger.log('Application started')
logger.error('Database connection failed')

Streams

Readable Streams

javascript
const { Readable } = require('stream')
const fs = require('fs')

// File stream
const readStream = fs.createReadStream('large-file.txt', {
  encoding: 'utf8',
  highWaterMark: 1024  // Buffer size
})

readStream.on('data', (chunk) => {
  console.log('Received chunk:', chunk.length, 'bytes')
})

readStream.on('end', () => {
  console.log('Stream ended')
})

readStream.on('error', (error) => {
  console.error('Stream error:', error.message)
})

// Custom readable stream
class NumberStream extends Readable {
  constructor(options) {
    super(options)
    this.current = 0
    this.max = 10
  }
  
  _read() {
    if (this.current < this.max) {
      this.push(`Number: ${this.current}\n`)
      this.current++
    } else {
      this.push(null)  // End stream
    }
  }
}

const numberStream = new NumberStream()
numberStream.on('data', (chunk) => {
  console.log(chunk.toString())
})

Writable Streams

javascript
const { Writable } = require('stream')
const fs = require('fs')

// File write stream
const writeStream = fs.createWriteStream('output.txt')

writeStream.write('Hello, ')
writeStream.write('Node.js!')
writeStream.end()

writeStream.on('finish', () => {
  console.log('Write completed')
})

// Custom writable stream
class ConsoleStream extends Writable {
  _write(chunk, encoding, callback) {
    console.log(`Writing: ${chunk.toString().toUpperCase()}`)
    callback()
  }
}

const consoleStream = new ConsoleStream()
consoleStream.write('hello world')
consoleStream.end()

Transform Streams

javascript
const { Transform } = require('stream')

// Custom transform stream
class UpperCaseTransform extends Transform {
  _transform(chunk, encoding, callback) {
    const upperChunk = chunk.toString().toUpperCase()
    callback(null, upperChunk)
  }
}

const upperTransform = new UpperCaseTransform()

// Pipe streams together
process.stdin
  .pipe(upperTransform)
  .pipe(process.stdout)

// File processing pipeline
const fs = require('fs')

fs.createReadStream('input.txt')
  .pipe(new UpperCaseTransform())
  .pipe(fs.createWriteStream('output.txt'))

NPM and Package Management

Package.json

json
{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "A sample Node.js application",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest",
    "build": "webpack --mode production",
    "lint": "eslint src/",
    "format": "prettier --write src/"
  },
  "keywords": ["node", "javascript", "api"],
  "author": "Your Name <your.email@example.com>",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.0.0",
    "dotenv": "^16.0.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.20",
    "jest": "^29.0.0",
    "eslint": "^8.0.0",
    "prettier": "^2.8.0"
  },
  "engines": {
    "node": ">=16.0.0",
    "npm": ">=8.0.0"
  }
}

NPM Commands

bash
# Initialize new project
npm init
npm init -y  # Skip questions

# Install packages
npm install express
npm install --save express          # Same as above
npm install --save-dev nodemon      # Development dependency
npm install -g nodemon              # Global installation

# Install from package.json
npm install
npm ci  # Clean install (faster, for CI/CD)

# Update packages
npm update
npm update express
npm outdated  # Check for outdated packages

# Remove packages
npm uninstall express
npm uninstall --save-dev nodemon

# List packages
npm list
npm list --depth=0  # Top-level only
npm list -g         # Global packages

# Run scripts
npm start
npm run dev
npm test

# Publish package
npm login
npm publish
npm version patch  # Increment version

Environment Variables

javascript
// .env file
NODE_ENV=development
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key
API_KEY=your-api-key

// Load environment variables
require('dotenv').config()

// Use environment variables
const port = process.env.PORT || 3000
const dbUrl = process.env.DATABASE_URL
const jwtSecret = process.env.JWT_SECRET

console.log('Environment:', process.env.NODE_ENV)
console.log('Port:', port)

Error Handling

Try-Catch with Async/Await

javascript
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`)
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    
    const userData = await response.json()
    return userData
  } catch (error) {
    console.error('Error fetching user data:', error.message)
    throw error  // Re-throw if needed
  }
}

// Usage
async function main() {
  try {
    const user = await fetchUserData(123)
    console.log('User:', user)
  } catch (error) {
    console.error('Failed to get user:', error.message)
  }
}

Global Error Handling

javascript
// Uncaught exceptions
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error)
  process.exit(1)
})

// Unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason)
  process.exit(1)
})

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully')
  server.close(() => {
    console.log('Process terminated')
  })
})

process.on('SIGINT', () => {
  console.log('SIGINT received, shutting down gracefully')
  process.exit(0)
})

Custom Error Classes

javascript
class ValidationError extends Error {
  constructor(message, field) {
    super(message)
    this.name = 'ValidationError'
    this.field = field
    this.statusCode = 400
  }
}

class NotFoundError extends Error {
  constructor(resource) {
    super(`${resource} not found`)
    this.name = 'NotFoundError'
    this.statusCode = 404
  }
}

// Usage
function validateUser(user) {
  if (!user.email) {
    throw new ValidationError('Email is required', 'email')
  }
  
  if (!user.name) {
    throw new ValidationError('Name is required', 'name')
  }
}

try {
  validateUser({ name: 'John' })
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Validation error in ${error.field}: ${error.message}`)
  } else {
    console.error('Unexpected error:', error.message)
  }
}

Testing

Unit Testing with Jest

javascript
// math.js
function add(a, b) {
  return a + b
}

function subtract(a, b) {
  return a - b
}

function divide(a, b) {
  if (b === 0) {
    throw new Error('Division by zero')
  }
  return a / b
}

module.exports = { add, subtract, divide }

// math.test.js
const { add, subtract, divide } = require('./math')

describe('Math functions', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(add(1, 2)).toBe(3)
  })
  
  test('subtracts 5 - 3 to equal 2', () => {
    expect(subtract(5, 3)).toBe(2)
  })
  
  test('divides 10 / 2 to equal 5', () => {
    expect(divide(10, 2)).toBe(5)
  })
  
  test('throws error when dividing by zero', () => {
    expect(() => divide(10, 0)).toThrow('Division by zero')
  })
})

// Async testing
describe('Async functions', () => {
  test('async function resolves', async () => {
    const result = await fetchData()
    expect(result).toHaveProperty('id')
  })
  
  test('promise resolves', () => {
    return fetchData().then(data => {
      expect(data).toHaveProperty('id')
    })
  })
})

Mocking

javascript
// userService.js
const axios = require('axios')

async function getUser(id) {
  const response = await axios.get(`/api/users/${id}`)
  return response.data
}

module.exports = { getUser }

// userService.test.js
const axios = require('axios')
const { getUser } = require('./userService')

jest.mock('axios')
const mockedAxios = axios

describe('User Service', () => {
  test('should fetch user', async () => {
    const userData = { id: 1, name: 'John Doe' }
    mockedAxios.get.mockResolvedValue({ data: userData })
    
    const result = await getUser(1)
    
    expect(mockedAxios.get).toHaveBeenCalledWith('/api/users/1')
    expect(result).toEqual(userData)
  })
})

Performance and Optimization

Clustering

javascript
const cluster = require('cluster')
const http = require('http')
const numCPUs = require('os').cpus().length

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`)
  
  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork()
  }
  
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`)
    cluster.fork()  // Restart worker
  })
} else {
  // Worker process
  const server = http.createServer((req, res) => {
    res.writeHead(200)
    res.end(`Hello from worker ${process.pid}`)
  })
  
  server.listen(3000, () => {
    console.log(`Worker ${process.pid} started`)
  })
}

Memory Management

javascript
// Monitor memory usage
function logMemoryUsage() {
  const usage = process.memoryUsage()
  console.log({
    rss: `${Math.round(usage.rss / 1024 / 1024)} MB`,
    heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)} MB`,
    heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)} MB`,
    external: `${Math.round(usage.external / 1024 / 1024)} MB`
  })
}

setInterval(logMemoryUsage, 5000)

// Force garbage collection (for debugging)
if (global.gc) {
  global.gc()
} else {
  console.log('Garbage collection unavailable. Pass --expose-gc when launching node.')
}

Profiling

javascript
// CPU profiling
const fs = require('fs')

// Start profiling
const profiler = require('v8-profiler-next')
profiler.startProfiling('CPU profile')

// Your application code here
setTimeout(() => {
  // Stop profiling
  const profile = profiler.stopProfiling('CPU profile')
  
  profile.export((error, result) => {
    fs.writeFileSync('profile.cpuprofile', result)
    profile.delete()
    console.log('Profile saved to profile.cpuprofile')
  })
}, 10000)

Security Best Practices

Input Validation

javascript
const validator = require('validator')

function validateUserInput(data) {
  const errors = []
  
  // Email validation
  if (!data.email || !validator.isEmail(data.email)) {
    errors.push('Valid email is required')
  }
  
  // Password validation
  if (!data.password || data.password.length < 8) {
    errors.push('Password must be at least 8 characters')
  }
  
  // Sanitize input
  const sanitizedData = {
    email: validator.normalizeEmail(data.email),
    name: validator.escape(data.name),
    age: validator.toInt(data.age)
  }
  
  return { errors, sanitizedData }
}

Environment Security

javascript
// Secure environment configuration
const config = {
  port: process.env.PORT || 3000,
  nodeEnv: process.env.NODE_ENV || 'development',
  
  // Database
  dbUrl: process.env.DATABASE_URL || 'mongodb://localhost:27017/app',
  
  // JWT
  jwtSecret: process.env.JWT_SECRET,
  jwtExpiry: process.env.JWT_EXPIRY || '1h',
  
  // API keys (never log these)
  apiKey: process.env.API_KEY,
  
  // Security headers
  corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3000'
}

// Validate required environment variables
const requiredEnvVars = ['JWT_SECRET', 'DATABASE_URL']
const missingEnvVars = requiredEnvVars.filter(envVar => !process.env[envVar])

if (missingEnvVars.length > 0) {
  console.error('Missing required environment variables:', missingEnvVars)
  process.exit(1)
}

module.exports = config

Production Deployment

Process Management with PM2

bash
# Install PM2
npm install -g pm2

# Start application
pm2 start app.js
pm2 start app.js --name "my-app"

# Start with cluster mode
pm2 start app.js -i max  # Use all CPU cores
pm2 start app.js -i 4    # Use 4 instances

# Process management
pm2 list
pm2 stop my-app
pm2 restart my-app
pm2 delete my-app

# Monitoring
pm2 monit
pm2 logs
pm2 logs my-app

# Auto-restart on file changes
pm2 start app.js --watch

# Startup script
pm2 startup
pm2 save

PM2 Ecosystem File

javascript
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'my-app',
    script: './app.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'development',
      PORT: 3000
    },
    env_production: {
      NODE_ENV: 'production',
      PORT: 8080
    },
    error_file: './logs/err.log',
    out_file: './logs/out.log',
    log_file: './logs/combined.log',
    time: true
  }]
}

// Start with ecosystem file
// pm2 start ecosystem.config.js
// pm2 start ecosystem.config.js --env production

Docker Deployment

dockerfile
# Dockerfile
FROM node:18-alpine

# Create app directory
WORKDIR /usr/src/app

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodeuser -u 1001

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production && npm cache clean --force

# Copy app source
COPY --chown=nodeuser:nodejs . .

# Switch to non-root user
USER nodeuser

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# Start application
CMD ["node", "app.js"]

Logging in Production

javascript
const winston = require('winston')

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'my-app' },
  transports: [
    new winston.transports.File({ 
      filename: 'logs/error.log', 
      level: 'error' 
    }),
    new winston.transports.File({ 
      filename: 'logs/combined.log' 
    })
  ]
})

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple()
  }))
}

// Usage
logger.info('Application started', { port: 3000 })
logger.error('Database connection failed', { error: error.message })
logger.warn('High memory usage detected', { usage: process.memoryUsage() })

module.exports = logger

This comprehensive Node.js tutorial covers server-side JavaScript development from fundamentals to production deployment. Practice building real applications to master these concepts.

VitePress Development Guide