wordpress_blog

This is a dynamic to static website.

Node.js Crash Course (2024 Revamp)

Learning From Youtube Channel Traversy Media
Video: Node.js Crash Course (2024 Revamp)
Thank you.

Timestamps:
00:00:00 – Intro & Slides
00:12:18 – Installation
00:13:22 – Node REPL
00:15:05 – Setup & package.json Init
00:16:51 – Running JavaScript Files
00:19:36 – CommonJS Modules
00:23:13 – ES Modules
00:27:20 – HTTP Module & Create Server
00:35:06 – NPM Scripts
00:36:15 – NPM Modules & Nodemon
00:38:45 – .gitignore File
00:41:06 – Environment Variables & .env
00:44:00 – Req Object
00:46:10 – Marking Requests From Postman
00:47:28 – Simple Routing
00:51:52 – Loading Files
00:59:45 – Building a Simple API
01:10:27 – Middleware
01:13:24 – Cleanup (Middleware & Handlers)
01:19:08 – Get Req Body For POST
01:24:20 – File System Module
01:33:43 – Path Module
01:39:50 – OS Module
01:42:13 – URL Module
01:47:46 – Crypto Module
01:54:08 – Emitting Events
01:59:04 – Process Object

Intro & Slides

What Is Node.js?

  • Node.js is an open-source JavaScript runtime
  • Uses the V8 JavaScript engine that Google Chrome uses
  • Mostly used for developing server-side & networking apps/apis
  • Takes JavaScript out of the browser
  • Fast, scalable and popular in many areas of the industry

Covered In This Course

  • What is Node.js and how does it work?
  • Installation, setup, package.json, npm, etc
  • Modules import/export (CommonJS & ES Modules)
  • HTTP Module, req/res, routing, serving JSON/HTML
  • Custom middleware
  • Other Core Modules – fs, path, url, events, process, os

Prerequisites

  • JavaScript fundamentals (Functions, loops, objects, classes, etc)
  • Asynchronous programming – Promises, callbacks, async/await
  • HTTP Basics (Methods, status codes, etc)
  • How JSON APIs work
  • NPM (Node Package Manager)

How Node.js Works

  • V8 JS Engine
  • Non-Blocking
  • Single Threaded
  • Event Loop

What is Node.js Used For?

  • APIs
  • Server-rendered apps
  • Real-time applications
  • Micronservices
  • Command Line Tools
  • Bots
  • Web scraping
  • Web Servers

Not good for CPU-intensive tasks

Installation

// Terminal
node --version
npm --version

Node REPL

// Terminal
> console.log('Hello World')
Hello World
undefined
> const name = 'John'
undefined
> console.log(name)
John
undefined
> const greet = () => `Hello ${name}`
undefined
> greet()
'Hello John'

Setup & package.json Init

// Terminal
mkdir nodejs-crash-2024
cd nodejs-crash-2024
code .
// VSCODE - Terminal
方式一:
npm init -y
// 方式二:
npm init

Running JavaScript Files

  • 建立 index.js 檔案
// index.js
console.log('Hello World'):
// Terminal
// 方式一:
node index.js
// 方式二:
node index

CommonJS Modules

  • 建立 utils.js 檔案
// utils.js
function generateRandomNumber() {
  return Math.floor(Math.random() * 100) + 1;
}

function celclusToFahrenheit(celcius) {
  return (celcius * 9) / 5 + 32;
}

module.exports = {
  generateRandomNumber,
  celclusToFahrenheit,
};
// index.js
const { generateRandomNumber, celclusToFahrenheit } = require('./utils');
console.log(`Random Number: ${generateRandomNumber()}`);
console.log(`Celcius to fahrenhiet: ${celclusToFahrenheit(0)}`);
// Terminal
node index

ES Modules

  • 在 package.json 檔案加上 type 屬性、值
// package.json
"type": "module"
  • 建立 postController.js 檔案
// postController.js - 1
const posts = [
  { id: 1, title: 'Post One' },
  { id: 2, title: 'Post Two' },
];

export const getPosts = () => posts;
// index.js - 1
import { getPosts  } from "./postController.js";

console.log(getPosts());

// const { generateRandomNumber, celclusToFahrenheit } = require('./utils');
// console.log(`Random Number: ${generateRandomNumber()}`);
// console.log(`Celcius to fahrenhiet: ${celclusToFahrenheit(0)}`);
// postController.js - 2
const posts = [
  { id: 1, title: 'Post One' },
  { id: 2, title: 'Post Two' },
];

const getPosts = () => posts;

export const getPostsLength = () => posts.length;

export default getPosts;
// index.js - 2
import getPosts, { getPostsLength } from "./postController.js";

console.log(getPosts());

console.log(`Posts Length: ${getPostsLength()}`);

// const { generateRandomNumber, celclusToFahrenheit } = require('./utils');
// console.log(`Random Number: ${generateRandomNumber()}`);
// console.log(`Celcius to fahrenhiet: ${celclusToFahrenheit(0)}`);

HTTP Module & Create Server

  • 建立 server.js 檔案
// server.js - 1
import http from 'http';
const PORT = 8000;

const server = http.createServer((req, res) => {
  res.write('Hello World!');
  res.end();
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});
// server.js - 2
import http from 'http';
const PORT = 8000;

const server = http.createServer((req, res) => {
  res.end('Hello World!');
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});
// server.js - 3
import http from 'http';
const PORT = 8000;

const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  
  res.end('<h1>Hello World!</h1>');
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});
// server.js - 4
import http from 'http';
const PORT = 8000;

const server = http.createServer((req, res) => {
  // res.setHeader('Content-Type', 'text/html');
  // res.statusCode = 404;

  res.writeHead(500, { 'Content-Type': 'application/json' })
  res.end(JSON.stringify({ message: 'Server Error' }));
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});

NPM Scripts、NPM Modules & Nodemon、.gitignore File

  • nodemon
  • 安裝 nodemon 套件 – npm i -D nodemon
  • 建立 .gitignore 檔案
  • 執行 nodemon
    npm start
// .gitignore
node_modules
// package.json
"start": "nodemon server.js",
// server.js
import http from 'http';
const PORT = 8000;

const server = http.createServer((req, res) => {
  // res.setHeader('Content-Type', 'text/html');
  // res.statusCode = 404;

  res.writeHead(200, { 'Content-Type': 'text/html' })
  res.end('<h1>Hello World</h1>');
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});

Environment Variables & .env

  • 建立 .env 檔案
  • 在 .gitignore 檔案加入 .env
  • 修改 package.json 檔案
  • 修改 server.js 檔案
// .env
PORT = 8000
// .gitignore
node_modules
.env
// package.json
"start": "nodemon --env-file=.env server.js",
// server.js
import http from 'http';
const PORT = process.env.PORT;

const server = http.createServer((req, res) => {
  // res.setHeader('Content-Type', 'text/html');
  // res.statusCode = 404;

  res.writeHead(200, { 'Content-Type': 'text/html' })
  res.end('<h1>Hello World</h1>');
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});

Req Object

// server.js
import http from 'http';
const PORT = process.env.PORT;

const server = http.createServer((req, res) => {
  // res.setHeader('Content-Type', 'text/html');
  // res.statusCode = 404;
  console.log(req.url);
  console.log(req.method);

  res.writeHead(200, { 'Content-Type': 'text/html' })
  res.end('<h1>Hello World</h1>');
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});

Marking Requests From Postman

Simple Routing

  • 修改 server.js 檔案
  • 使用 API 測試工具 Get 方法回傳資料
    http://localhost:8000/
// server.js - 1
import http from 'http';
const PORT = process.env.PORT;

const server = http.createServer((req, res) => {
  if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html' })
    res.end('<h1>Homepage</h1>');
  } else if (req.url === '/about') {
    res.writeHead(200, { 'Content-Type': 'text/html' })
    res.end('<h1>About</h1>');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/html' })
    res.end('<h1>Not Found</h1>');
  }

});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});
// server.js - 2
import http from 'http';
const PORT = process.env.PORT;

const server = http.createServer((req, res) => {
  try {
    // Check if GET request
    if (req.method === 'GET') {
      if (req.url === '/') {
        res.writeHead(200, { 'Content-Type': 'text/html' })
        res.end('<h1>Homepage</h1>');
      } else if (req.url === '/about') {
        res.writeHead(200, { 'Content-Type': 'text/html' })
        res.end('<h1>About</h1>');
      } else {
        res.writeHead(404, { 'Content-Type': 'text/html' })
        res.end('<h1>Not Found</h1>');
      }
    } else {
      throw new Error('Method not allowed')
    }
  } catch (error) {
    res.writeHead(500, { 'Content-Type': 'text/plain' })
        res.end('Server Error');
  }
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});

Loading Files

  • 建立 public 資料夾、裡面建立 index.html、about.html 檔案
// public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Homepage</title>
</head>
<body>
  <h1>Homepage</h1>
</body>
</html>
// public/about.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>About</title>
</head>
<body>
  <h1>About</h1>
</body>
</html>
// server.js
import http from 'http';
import fs from 'fs/promises';
import url from 'url';
import path from 'path';
const PORT = process.env.PORT;

// Get current path
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const server = http.createServer(async (req, res) => {
  try {
    // Check if GET request
    if (req.method === 'GET') {
      let filePath;
      if (req.url === '/') {
        filePath = path.join(__dirname, 'public', 'index.html');
      } else if (req.url === '/about') {
        filePath = path.join(__dirname, 'public', 'about.html');
      } else {
        throw new Error('Not Found')
      }

      const data = await fs.readFile(filePath);
      res.setHeader('Content-Type', 'text/html');
      res.write(data);
      res.end();
    } else {
      throw new Error('Method not allowed')
    }
  } catch (error) {
    res.writeHead(500, { 'Content-Type': 'text/plain' })
        res.end('Server Error');
  }
});

server.listen(8000, () => {
  console.log(`Server running on port ${PORT}`);
});

Building a Simple API

  • 建立 server2.js 檔案
  • 修改 package.json 檔案
// package.json
"start": "nodemon --env-file=.env server2.js"
// server2.js
import { createServer } from 'http';
const PORT = process.env.PORT;

const users = [
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Jane Doe' },
  { id: 3, name: 'Jim Doe' },
];

const server = createServer((req, res) => {
  if (req.url === '/api/users' && req.method === 'GET') {
    res.setHeader('Content-Type', 'application/json');
    res.write(JSON.stringify(users));
    res.end();
  } else if (req.url.match(/\/api\/users\/([0-9]+)/) && req.method === 'GET') {
    const id = req.url.split('/')[3];
    const user = users.find((user) => user.id === parseInt(id));
    res.setHeader('Content-Type', 'application/json');
    if (user) {
      res.write(JSON.stringify(user));
    } else {
      res.statusCode = 404;
      res.write(JSON.stringify({ message: 'User not found' }));
    }
    res.end();
  } else {
    res.setHeader('Content-Type', 'application/json');
    res.statusCode = 404;
    res.write(JSON.stringify({ message: 'Route not found' }));
    res.end();
  }
});

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

Middleware

// server2.js
import { createServer } from 'http';
const PORT = process.env.PORT;

const users = [
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Jane Doe' },
  { id: 3, name: 'Jim Doe' },
];

// Logger middleware
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
};

const server = createServer((req, res) => {
  logger(req, res, () => {
    if (req.url === '/api/users' && req.method === 'GET') {
      res.setHeader('Content-Type', 'application/json');
      res.write(JSON.stringify(users));
      res.end();
    } else if (req.url.match(/\/api\/users\/([0-9]+)/) && req.method === 'GET') {
      const id = req.url.split('/')[3];
      const user = users.find((user) => user.id === parseInt(id));
      res.setHeader('Content-Type', 'application/json');
      if (user) {
        res.write(JSON.stringify(user));
      } else {
        res.statusCode = 404;
        res.write(JSON.stringify({ message: 'User not found' }));
      }
      res.end();
    } else {
      res.setHeader('Content-Type', 'application/json');
      res.statusCode = 404;
      res.write(JSON.stringify({ message: 'Route not found' }));
      res.end();
    }
  });
});

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

Cleanup (Middleware & Handlers)

// server2.js
import { createServer } from 'http';
const PORT = process.env.PORT;

const users = [
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Jane Doe' },
  { id: 3, name: 'Jim Doe' },
];

// Logger middleware
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
};

// JSON middleware
const jsonMiddleware = (req, res, next) => {
  res.setHeader('Content-Type', 'application/json');
  next();
};

// Route handler for GET /api/users
const getUsersHandler = (req, res) => {
  res.write(JSON.stringify(users));
  res.end();
}

// Route handler for GET /api/:id
const getUserByIdHandler = (req, res) => {
  const id = req.url.split('/')[3];
  const user = users.find((user) => user.id === parseInt(id));

  if (user) {
    res.write(JSON.stringify(user));
  } else {
    res.statusCode = 404;
    res.write(JSON.stringify({ message: 'User not found' }));
  }
  res.end();
}

// Not found handler
const notFoundHandler = (req, res) => {
  res.statusCode = 404;
  res.write(JSON.stringify({ message: 'Route not found' }));
  res.end();
}

const server = createServer((req, res) => {
  logger(req, res, () => {
    jsonMiddleware(req, res, () => {
      if (req.url === '/api/users' && req.method === 'GET') {
        getUsersHandler(req, res);
      } else if (req.url.match(/\/api\/users\/([0-9]+)/) && req.method === 'GET') {
        getUserByIdHandler(req, res);
      } else {
        notFoundHandler(req, res)
      }
    })
  });
});

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

Get Req Body For POST

// server2.js
import { createServer } from 'http';
const PORT = process.env.PORT;

const users = [
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Jane Doe' },
  { id: 3, name: 'Jim Doe' },
];

// Logger middleware
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
};

// JSON middleware
const jsonMiddleware = (req, res, next) => {
  res.setHeader('Content-Type', 'application/json');
  next();
};

// Route handler for GET /api/users
const getUsersHandler = (req, res) => {
  res.write(JSON.stringify(users));
  res.end();
}

// Route handler for GET /api/:id
const getUserByIdHandler = (req, res) => {
  const id = req.url.split('/')[3];
  const user = users.find((user) => user.id === parseInt(id));

  if (user) {
    res.write(JSON.stringify(user));
  } else {
    res.statusCode = 404;
    res.write(JSON.stringify({ message: 'User not found' }));
  }
  res.end();
}

// Route handler for POST /api/users
const createUserHandler = (req, res) => {
  let body = '';
  // Listen for data
  req.on('data', (chunk) => {
    body += chunk.toString();
  });
  req.on('end', () => {
    const newUser = JSON.parse(body);
    users.push(newUser);
    res.statusCode = 201;
    res.write(JSON.stringify(newUser));
    res.end();
  })
};

// Not found handler
const notFoundHandler = (req, res) => {
  res.statusCode = 404;
  res.write(JSON.stringify({ message: 'Route not found' }));
  res.end();
}

const server = createServer((req, res) => {
  logger(req, res, () => {
    jsonMiddleware(req, res, () => {
      if (req.url === '/api/users' && req.method === 'GET') {
        getUsersHandler(req, res);
      } else if (req.url.match(/\/api\/users\/([0-9]+)/) && req.method === 'GET') {
        getUserByIdHandler(req, res);
      } else if (req.url === '/api/users' && req.method === 'POST') {
        createUserHandler(req, res);
      } else {
        notFoundHandler(req, res)
      }
    })
  });
});

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
// API 測試工具
{
  "id": 4,
  "name": "Brad"
}

File System Module

  • 建立 fsDemo.js 檔案
  • Node.js docs – File System
  • 建立 test.txt 檔案
  • 使用 Terminal – node fsDemo.js
// fsDemo.js
// import fs from 'fs';
import fs from 'fs/promises';

// readFile() - callback
// fs.readFile('./test.txt', 'utf8', (err, data) => {
//   if (err) throw err;
//   console.log(data);
// });

// readFileSync() - Synchronous version
// const data = fs.readFileSync('./test.txt', 'utf8');
// console.log(data);

// readFile() - Promise .then()
// fs.readFile('./test.txt', 'utf8')
//   .then((data) => console.log(data))
//   .catch((err) => console.log(err));

// readFile() - async/await
const readFile = async () => {
  try {
    const data = await fs.readFile('./test.txt', 'utf8');
    console.log(data);
  } catch (error) {
    console.log(error);
  }
};

// writeFile()

const writeFile = async () => {
  try {
    await fs.writeFile('./test.txt', 'Hello, I am writing to this file');
    console.log('File written to...');
  } catch (error) {
    console.log(error);
  }
};

// appendFile()
const appendFile = async () => {
  try {
    await fs.appendFile('./test.txt', '\nThis is appended text');
    console.log('File appended to...');
  } catch (error) {
    console.log(error);
  }
}

writeFile();
appendFile();
readFile();
// test.txt - 1
This is from the text file
// test.text - 2
Hello, I am writing to this file
This is appended text

Path Module

  • 建立 pathDemo.js 檔案
// pathDemo.js
import path from 'path';
import url from 'url';

const filePath = './dir1/dir2/test.txt';

// basename()
console.log(path.basename(filePath));

// dirname()
console.log(path.dirname(filePath));

// extname()
console.log(path.extname(filePath));

// parse()
console.log(path.parse(filePath));

const __filename = url.fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename);

// join()
const filePath2 = path.join(__dirname, 'dir1', 'dir2', 'test.txt');
console.log(filePath2);

// resolve()
const filePath3 = path.resolve(__dirname, 'dir1', 'dir2', 'test.txt');
console.log(filePath3);

OS Module

  • 建立 osDemo.js 檔案
// osDemo.js
import os from 'os';

// userInfo()
console.log(os.userInfo());

// totalmem()
console.log(os.totalmem());

// freemem()
console.log(os.freemem());

// cpus()
console.log(os.cpus());

URL Module

  • 建立 urlDemo.js 檔案
// urlDemo.js
import url from 'url';

const urlString = 'https://www.google.com/search?q=hello+world';

// URL Object
const urlObj = new URL(urlString);

console.log(urlObj.pathname);

// format()
console.log(url.format(urlObj));

// import.meta.url - file URL
console.log(import.meta.url);

// fileURLToPath()
console.log(url.fileURLToPath(import.meta.url));

console.log(urlObj.search);

const params = new URLSearchParams(urlObj.search);
console.log(params.get('q'));
params.append('limit', '5');
params.delete('limit');
console.log(params);

Crypto Module

  • 建立 cryptoDemo.js 檔案
// cryptoDemo.js
import crypto from 'crypto';

// createHash()
// const hash = crypto.createHash('sha256');
// hash.update('password1234');
// console.log(hash.digest('hex'));

// randomBytes()
// crypto.randomBytes(16, (err, buf) => {
//   if(err) throw err;
//   console.log(buf.toString('hex'));
// });

// createCipheriv & createDecipheriv
const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);

const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update('Hello, this is a secret message', 'utf8', 'hex');
encrypted += cipher.final('hex');
console.log(encrypted);

const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);

Emitting Events

  • 建立 eventsDemo.js 檔案
// eventsDemo.js
import { EventEmitter } from 'events';

const myEmitter = new EventEmitter();

function greetHandler(name) {
  console.log('Hello ' + name);
}

function goodbyeHandler(name) {
  console.log('Goodbye ' + name);
}

// Register event listeners
myEmitter.on('greet', greetHandler);
myEmitter.on('goodbye', goodbyeHandler);

// Emit events
myEmitter.emit('greet', 'John');
myEmitter.emit('goodbye', 'John');

// Error handling
myEmitter.on('error', (err) => {
  console.log('An Error Occured:', err);
});

// Simulate error
myEmitter.emit('error', new Error('Something went wrong'));

Process Object

  • 建立 processDemo.js
  • 終端機執行
    node processDemo.js
    node processDemo.js import
    node processDemo.js import -s
// processDemo.js
// argv
console.log(process.argv);
console.log(process.argv[3]);

// process.env
console.log(process.env.USERNAME);

// pid
console.log(process.pid);

// cwd()
console.log(process.cwd());

// title
console.log(process.title);

// memoryUsage()
console.log(process.memoryUsage());

// update()
console.log(process.uptime());

process.on('exit', (code) => {
  console.log(`About to exit with code: ${code}`);
});

// exit()
process.exit(0);

console.log('Hello from after exit');