Node.js

Node.js

Server-Side JavaScript

CSCI 4513

Week 13, Lecture 23

Today's Learning Objectives

Norse Mythology Connection

Hlidskjalf - Odin's High Seat

From his throne Hlidskjalf, Odin can observe all nine realms and respond to any event, just as a Node.js server listens for requests from anywhere in the world.

The Tale:

High in Asgard stands Hlidskjalf, Odin's magnificent throne from which he surveys all the nine realms. From this elevated seat, nothing escapes his noticeβ€”he sees events in Midgard (the mortal world), conflicts in Jotunheim (land of giants), celebrations in Alfheim (realm of elves), and activities across all other realms simultaneously.

When mortals pray, when giants scheme, when events unfold anywhere in the cosmos, Odin perceives them from Hlidskjalf. He doesn't merely watchβ€”he listens, processes information, and can dispatch responses through his ravens Huginn (Thought) and Muninn (Memory), who carry messages and gather intelligence across the realms. This high seat serves as the ultimate server, constantly running, always listening, ready to process requests and send responses to any corner of the nine worlds.

The Server's High Seat

// Hlidskjalf - The server that listens to all realms
const http = require('http');

const server = http.createServer((request, response) => {
    // Like Odin processing requests from across the nine realms
    console.log(`Request from: ${request.url}`);

    // Dispatch response (like sending the ravens)
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.end('<h1>Response from Hlidskjalf</h1>');
});

// Start listening on port 3000 (establish the high seat)
server.listen(3000, () => {
    console.log('Hlidskjalf established on port 3000...');
    console.log('Now observing all realms for requests...');
});

Reflection Questions

What is Backend Development?

The backend handles:

Data storage, business logic, authentication, server-side rendering, API requests

Client-Server Architecture

Client (Browser)              Server (Node.js)
    |                             |
    |  1. HTTP Request             |
    | ------------------------->   |
    |   (GET /users)               |
    |                             |
    |                      2. Process Request
    |                      3. Query Database
    |                      4. Prepare Response
    |                             |
    |  5. HTTP Response            |
    | <-------------------------   |
    |   (JSON data)                |
    |                             |

What is Node.js?

Key Point: Node.js lets you use JavaScript for backend development!

Why Use Node.js?

βœ… Advantages

  • Same language (JavaScript) for frontend and backend
  • Fast and efficient (async I/O)
  • Large ecosystem (npm packages)
  • Great for real-time applications
  • Active community

⚠️ Limitations

  • Not ideal for CPU-intensive tasks
  • Callback hell (mitigated by async/await)
  • Single-threaded (but has workarounds)

Node.js vs Browser JavaScript

// ❌ Browser APIs - NOT available in Node
window.alert('Hello');
document.querySelector('#app');
localStorage.setItem('key', 'value');
fetch('https://api.example.com');  // Available but different

// βœ… Node.js APIs - NOT available in browser
const fs = require('fs');
const http = require('http');
const path = require('path');
process.env.NODE_ENV;

// βœ… Available in BOTH
console.log('Hello');
setTimeout(() => {}, 1000);
const data = JSON.parse('{"key": "value"}');

Installing Node.js

1. Download from: nodejs.org

2. Choose version:

  • LTS (Long Term Support) - Recommended for most users
  • Current - Latest features

3. Verify installation:

node --version
npm --version

Running Node Code

// Method 1: REPL (Read-Eval-Print Loop)
// Just type 'node' in terminal
$ node
> console.log('Hello from Node!')
Hello from Node!
> 2 + 2
4
> .exit  // or Ctrl+C twice


// Method 2: Run a file
// Create app.js, then run:
$ node app.js


// Method 3: Run with nodemon (auto-restart on changes)
$ npm install -g nodemon
$ nodemon app.js

Your First Node Program

// app.js
console.log('Hello from Node.js!');

const greeting = (name) => {
    return `Welcome, ${name}!`;
};

console.log(greeting('Alice'));
console.log(greeting('Bob'));

// Run with: node app.js

Output:

Hello from Node.js!
Welcome, Alice!
Welcome, Bob!

Node Modules System

Importing Modules (CommonJS)

// Built-in modules
const fs = require('fs');
const http = require('http');
const path = require('path');

// npm packages (install first with npm install)
const express = require('express');
const lodash = require('lodash');

// Your own modules
const myModule = require('./myModule');
const utils = require('./utils/helpers');

// Specific exports
const { readFile } = require('fs');
const { join } = require('path');

Creating Your Own Modules

// math.js - Export functions
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;

module.exports = {
    add,
    subtract,
    multiply
};

// app.js - Import and use
const math = require('./math');

console.log(math.add(5, 3));      // 8
console.log(math.multiply(4, 2)); // 8

File System (fs) Module

Read and write files from your Node application

const fs = require('fs');

// Read file (synchronous - blocks execution)
const data = fs.readFileSync('input.txt', 'utf8');
console.log(data);

// Read file (asynchronous - doesn't block)
fs.readFile('input.txt', 'utf8', (err, data) => {
    if (err) {
        console.error('Error reading file:', err);
        return;
    }
    console.log(data);
});

// Modern: Promises version
const fsPromises = require('fs').promises;
const data = await fsPromises.readFile('input.txt', 'utf8');

Writing Files

const fs = require('fs');

// Write file (replaces existing content)
fs.writeFile('output.txt', 'Hello, World!', (err) => {
    if (err) {
        console.error('Error writing file:', err);
        return;
    }
    console.log('File written successfully!');
});

// Append to file
fs.appendFile('log.txt', 'New log entry\n', (err) => {
    if (err) throw err;
    console.log('Log updated!');
});

// Synchronous version
fs.writeFileSync('output.txt', 'Hello, World!');

Working with Directories

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

// Read directory contents
fs.readdir('./files', (err, files) => {
    if (err) throw err;
    console.log(files);  // Array of filenames
});

// Create directory
fs.mkdir('./new-folder', (err) => {
    if (err) throw err;
    console.log('Directory created!');
});

// Check if file/directory exists
if (fs.existsSync('./my-file.txt')) {
    console.log('File exists!');
}

HTTP Module - Creating Servers

const http = require('http');

// Create a server
const server = http.createServer((request, response) => {
    // This function runs for every incoming request
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.end('<h1>Hello from Node.js Server!</h1>');
});

// Start listening on port 3000
server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

Visit http://localhost:3000 in your browser!

Request and Response Objects

const http = require('http');

const server = http.createServer((req, res) => {
    // Request object properties
    console.log('Method:', req.method);       // GET, POST, etc.
    console.log('URL:', req.url);             // /about, /users, etc.
    console.log('Headers:', req.headers);     // Request headers

    // Response methods
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write('<h1>Welcome</h1>');
    res.write('<p>Path: ' + req.url + '</p>');
    res.end();
});

server.listen(3000);

Routing - Different URLs

const http = require('http');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });

    if (req.url === '/') {
        res.end('<h1>Home Page</h1>');
    } else if (req.url === '/about') {
        res.end('<h1>About Page</h1>');
    } else if (req.url === '/contact') {
        res.end('<h1>Contact Page</h1>');
    } else {
        res.writeHead(404);
        res.end('<h1>404 - Page Not Found</h1>');
    }
});

server.listen(3000);

Serving HTML Files

const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
    let filePath = './public' + req.url;
    if (filePath === './public/') {
        filePath = './public/index.html';
    }

    fs.readFile(filePath, (err, content) => {
        if (err) {
            res.writeHead(404);
            res.end('<h1>404 - File Not Found</h1>');
        } else {
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.end(content);
        }
    });
});

server.listen(3000);

Setting Content Types

const path = require('path');

// Get file extension
const extname = path.extname(filePath);

// Set appropriate content type
let contentType = 'text/html';

switch (extname) {
    case '.js':
        contentType = 'text/javascript';
        break;
    case '.css':
        contentType = 'text/css';
        break;
    case '.json':
        contentType = 'application/json';
        break;
    case '.png':
        contentType = 'image/png';
        break;
}

res.writeHead(200, { 'Content-Type': contentType });

Debugging Node Applications

Using the Node Debugger

// app.js
const calculate = (a, b) => {
    debugger;  // Program pauses here when debugging
    const result = a + b;
    return result;
};

console.log(calculate(5, 3));

// Run with debugger:
// $ node inspect app.js

// Or in Chrome DevTools:
// $ node --inspect-brk app.js
// Then open chrome://inspect in Chrome

VS Code Debugging

1. Set breakpoints: Click left of line numbers

2. Press F5 to start debugging

3. Use debug controls:

  • Continue (F5)
  • Step Over (F10)
  • Step Into (F11)
  • Step Out (Shift+F11)

4. Inspect variables in the sidebar

Environment Variables

⚠️ Never commit .env files to git! Add to .gitignore

Using Environment Variables

// Access environment variables
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;

console.log(`Server will run on port ${port}`);

// Set when running (Linux/Mac):
$ PORT=8080 node app.js

// Set when running (Windows):
$ set PORT=8080 && node app.js

Using dotenv Package

// 1. Install dotenv
$ npm install dotenv

// 2. Create .env file
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
API_KEY=your_secret_api_key_here

// 3. Load in your app
require('dotenv').config();

const port = process.env.PORT;
const dbUrl = process.env.DATABASE_URL;

// 4. Add .env to .gitignore
echo ".env" >> .gitignore

Project: Basic Informational Site

Build a simple website with Node.js:

  • Create an HTTP server
  • Serve different HTML pages (index, about, contact)
  • Handle 404 errors for unknown routes
  • Serve CSS files
  • Use the file system module to read files

No frameworks! Pure Node.js only

Project Structure

basic-informational-site/
β”œβ”€β”€ index.js              # Server file
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ index.html        # Home page
β”‚   β”œβ”€β”€ about.html        # About page
β”‚   β”œβ”€β”€ contact-me.html   # Contact page
β”‚   β”œβ”€β”€ 404.html          # 404 error page
β”‚   └── styles.css        # Stylesheet
└── .gitignore

Starter Code

// index.js
const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
    // Your code here:
    // 1. Determine which file to serve based on req.url
    // 2. Read the file with fs.readFile()
    // 3. Set appropriate headers
    // 4. Send the file content or 404
});

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

Node.js Core Concepts Review

Common Node.js Use Cases

Web Servers

  • REST APIs
  • GraphQL servers
  • Server-side rendering
  • Microservices

Real-Time Apps

  • Chat applications
  • Collaboration tools
  • Gaming servers

Build Tools

  • Webpack
  • Vite
  • Task runners

Command Line Tools

  • npm packages
  • Scripts
  • Automation

Node.js Best Practices

Today's Takeaways

You Can Now:

Next Steps

πŸ“… Next Class: Express.js - A better way to build servers

πŸ“ Homework: Basic Informational Site Project

  • Build a multi-page website using pure Node.js
  • Serve HTML, CSS, and handle routing
  • Implement 404 error handling
  • Deploy to a hosting service (optional)

🎯 Coming Soon: Express framework will make this much easier!