← Back to Curriculum

Node.js Fundamentals and Environment Setup

šŸ“š Lesson 1 of 2 ā±ļø 45 min

Node.js Fundamentals and Environment Setup

45 min

Node.js is a JavaScript runtime built on Chrome's V8 engine that enables server-side JavaScript development.

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient for data-intensive applications.

The Node.js ecosystem includes npm (Node Package Manager) for package management and a vast library of open-source packages.

Setting up a proper development environment includes Node.js installation, version management with nvm, and IDE configuration.

Node.js's single-threaded event loop handles asynchronous operations efficiently, allowing it to handle many concurrent connections without the overhead of thread management.

Understanding Node.js's module system (CommonJS and ES modules) is fundamental to organizing and sharing code in Node.js applications.

Key Concepts

  • Node.js enables JavaScript to run on the server using V8 engine.
  • Event-driven, non-blocking I/O model enables high concurrency.
  • npm is the package manager for the Node.js ecosystem.
  • Node.js uses an event loop for asynchronous operations.
  • CommonJS and ES modules are the two module systems in Node.js.

Learning Objectives

Master

  • Setting up Node.js development environment
  • Understanding Node.js's event-driven architecture
  • Using npm for package management
  • Working with Node.js core modules (fs, http, path)

Develop

  • Server-side JavaScript thinking
  • Understanding asynchronous programming
  • Node.js ecosystem awareness

Tips

  • Use nvm (Node Version Manager) to manage multiple Node.js versions.
  • Always initialize projects with npm init to create package.json.
  • Use .gitignore to exclude node_modules from version control.
  • Set up nodemon for automatic server restarts during development.

Common Pitfalls

  • Blocking the event loop with synchronous operations.
  • Not handling errors in asynchronous operations properly.
  • Installing packages globally when local installation would suffice.
  • Forgetting to add node_modules to .gitignore.

Summary

  • Node.js enables server-side JavaScript development.
  • Event-driven, non-blocking I/O enables high performance.
  • npm manages packages and dependencies.
  • Understanding the event loop is crucial for Node.js development.

Exercise

Create a basic Node.js project structure with proper package.json configuration and demonstrate basic Node.js concepts.

// package.json
{
  "name": "nodejs-fundamentals",
  "version": "1.0.0",
  "description": "Learning Node.js fundamentals",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest"
  },
  "keywords": ["nodejs", "javascript", "backend"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1",
    "jest": "^29.6.2"
  }
}

// index.js - Basic Node.js concepts demonstration
const http = require('http');
const fs = require('fs').promises;
const path = require('path');

class NodeJSFundamentals {
  constructor() {
    this.server = null;
    this.port = process.env.PORT || 3000;
  }

  // Demonstrate event-driven programming
  createEventEmitter() {
    const EventEmitter = require('events');
    const myEmitter = new EventEmitter();

    myEmitter.on('userAction', (action, timestamp) => {
      console.log(`User performed: ${action} at ${timestamp}`);
    });

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

    // Emit events
    myEmitter.emit('userAction', 'login', new Date().toISOString());
    myEmitter.emit('userAction', 'logout', new Date().toISOString());

    return myEmitter;
  }

  // Demonstrate async/await with file operations
  async demonstrateAsyncOperations() {
    try {
      console.log('=== Async Operations Demo ===');
      
      // Create a temporary file
      const tempContent = 'Hello from Node.js!';
      const tempFile = path.join(__dirname, 'temp.txt');
      
      await fs.writeFile(tempFile, tempContent, 'utf8');
      console.log('File created successfully');
      
      // Read the file
      const readContent = await fs.readFile(tempFile, 'utf8');
      console.log('File content:', readContent);
      
      // Delete the file
      await fs.unlink(tempFile);
      console.log('File deleted successfully');
      
    } catch (error) {
      console.error('Error in async operations:', error.message);
    }
  }

  // Demonstrate streams
  demonstrateStreams() {
    console.log('\n=== Streams Demo ===');
    
    const { Readable, Writable, Transform } = require('stream');
    
    // Create a readable stream
    const readable = new Readable({
      read() {}
    });
    
    // Create a transform stream
    const transform = new Transform({
      transform(chunk, encoding, callback) {
        const transformed = chunk.toString().toUpperCase();
        callback(null, transformed);
      }
    });
    
    // Create a writable stream
    const writable = new Writable({
      write(chunk, encoding, callback) {
        console.log('Received:', chunk.toString());
        callback();
      }
    });
    
    // Pipe the streams together
    readable.pipe(transform).pipe(writable);
    
    // Push data to readable stream
    readable.push('hello world');
    readable.push('node.js is awesome');
    readable.push(null); // End the stream
  }

  // Demonstrate Buffer operations
  demonstrateBuffers() {
    console.log('\n=== Buffer Demo ===');
    
    // Create buffers
    const buffer1 = Buffer.from('Hello');
    const buffer2 = Buffer.from(' World');
    
    // Concatenate buffers
    const combined = Buffer.concat([buffer1, buffer2]);
    console.log('Combined buffer:', combined.toString());
    
    // Buffer to JSON
    const bufferInfo = {
      length: combined.length,
      content: combined.toString(),
      hex: combined.toString('hex'),
      base64: combined.toString('base64')
    };
    
    console.log('Buffer information:', bufferInfo);
  }

  // Create a basic HTTP server
  createServer() {
    this.server = http.createServer((req, res) => {
      const { method, url } = req;
      
      console.log(`${method} ${url}`);
      
      // Set CORS headers
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
      
      if (method === 'OPTIONS') {
        res.writeHead(200);
        res.end();
        return;
      }
      
      // Handle different routes
      if (method === 'GET' && url === '/') {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
          message: 'Welcome to Node.js Fundamentals!',
          timestamp: new Date().toISOString(),
          features: ['Event-driven', 'Non-blocking I/O', 'Streams', 'Buffers']
        }));
      } else if (method === 'GET' && url === '/health') {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
          status: 'healthy',
          uptime: process.uptime(),
          memory: process.memoryUsage()
        }));
      } else {
        res.writeHead(404, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Not Found' }));
      }
    });
    
    this.server.listen(this.port, () => {
      console.log(`šŸš€ Server running at http://localhost:${this.port}`);
      console.log('Available endpoints:');
      console.log('  GET / - Welcome message');
      console.log('  GET /health - Health check');
    });
    
    return this.server;
  }

  // Demonstrate process management
  demonstrateProcessManagement() {
    console.log('\n=== Process Management Demo ===');
    
    console.log('Process ID:', process.pid);
    console.log('Node.js version:', process.version);
    console.log('Platform:', process.platform);
    console.log('Architecture:', process.arch);
    console.log('Current working directory:', process.cwd());
    console.log('Environment variables:', Object.keys(process.env).length);
    
    // Handle process signals
    process.on('SIGINT', () => {
      console.log('\nšŸ›‘ Received SIGINT, shutting down gracefully...');
      if (this.server) {
        this.server.close(() => {
          console.log('Server closed');
          process.exit(0);
        });
      } else {
        process.exit(0);
      }
    });
    
    process.on('SIGTERM', () => {
      console.log('\nšŸ›‘ Received SIGTERM, shutting down gracefully...');
      if (this.server) {
        this.server.close(() => {
          console.log('Server closed');
          process.exit(0);
        });
      } else {
        process.exit(0);
      }
    });
  }

  async run() {
    console.log('šŸŽÆ Node.js Fundamentals Demonstration\n');
    
    // Run all demonstrations
    this.createEventEmitter();
    await this.demonstrateAsyncOperations();
    this.demonstrateStreams();
    this.demonstrateBuffers();
    this.createServer();
    this.demonstrateProcessManagement();
    
    console.log('\nāœ… All demonstrations completed!');
    console.log('Press Ctrl+C to stop the server');
  }
}

// Run the demonstration
if (require.main === module) {
  const demo = new NodeJSFundamentals();
  demo.run().catch(console.error);
}

module.exports = NodeJSFundamentals;

Exercise Tips

  • Use 'npm init -y' for quick project setup.
  • Install nodemon as a dev dependency for auto-restart.
  • Test your setup by running 'node index.js'.
  • Explore Node.js core modules in the official documentation.

Code Editor

Output