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
- Postman
- 替代方法: Hoppscotch
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');