# <span style="color: blue;">Back End Development and APIs (Phát triển Back End và API)</span>
## 1. <span style="color: brown;">Managing Packages with NPM (Quản lý các gói với NPM)</span>
### a. <span style="color: red;">How to Use package.json, the Core of Any Node.js Project or npm Package (Cách sử dụng package.json, Core của bất kỳ dự án Node.js hoặc gói npm nào)</span>
### ***Phân tích đề:

### Đề bài bảo chúng ta truy cập vào `package.json`

### `package.json` là tệp cấu hình của dự án Node.js, chứa thông tin như:
- name: Tên dự án.
- version: Phiên bản dự án.
- description: Mô tả ngắn gọn.
- main: Tệp chính để chạy ứng dụng.
- scripts: Các lệnh để chạy, build, test,...
- dependencies: Thư viện cần thiết khi chạy.
- devDependencies: Thư viện chỉ dùng khi phát triển.
- author: Tác giả dự án.
- license: Giấy phép sử dụng.
### ***Bắt đầu làm:

### Thêm tên của bạn với tư cách là tác giả của dự án vào tệp `package.json` .

### Trường `author` trong tệp package.json dùng để ghi lại tên của người tạo dự án.

### Vào `terminal` để chạy lệnh, lấy link và submit bài thành công
### b. <span style="color: red;">Add a Description to Your package.json (Thêm mô tả vào package.json của bạn)</span>

### Trong tệp `package.json`, trường `description` dùng để mô tả ngắn gọn về dự án của bạn. Nó giúp người khác (hoặc chính bạn sau này) hiểu nhanh dự án dùng để làm gì.

### Chạy tương tự như câu a để hoàn thành bài tập
### c. <span style="color: red;">Add Keywords to Your package.json (Thêm từ khóa vào package.json của bạn)</span>
### `Keywords` field trong tệp package.json được dùng để liệt kê các từ khóa mô tả dự án của bạn. Các từ khóa này giúp người dùng dễ dàng tìm thấy gói (package) của bạn khi tìm kiếm trên npm.
🔑 Cấu trúc của `keywords` field:
- Được định dạng dưới dạng mảng ([]).
- Mỗi từ khóa là một chuỗi (string) nằm trong dấu ngoặc kép (" ").
- Các từ khóa được ngăn cách bằng dấu phẩy (,).

### Chạy tương tự như câu a để hoàn thành bài tập

### d. <span style="color: red;">Add a License to Your package.json (Thêm Giấy phép vào package.json của bạn)</span>
### `License` field trong tệp package.json được sử dụng để xác định giấy phép sử dụng cho dự án của bạn.
🔑 Ý nghĩa:
- Trường "license" cho biết người dùng được phép làm gì với dự án (sao chép, sửa đổi, phân phối...).
- Việc khai báo giấy phép giúp bảo vệ quyền lợi của bạn và làm rõ quyền sử dụng cho người khác.
- Nếu không có giấy phép, mặc định là bản quyền thuộc về tác giả, và người khác không được phép sử dụng, sao chép hoặc phân phối.
📜 Các loại giấy phép phổ biến:
- "MIT": Cho phép sử dụng, sửa đổi, phân phối, miễn phí, nhưng phải kèm theo bản quyền.
- "BSD-3-Clause": Tương tự MIT, nhưng có thêm điều khoản về trách nhiệm.
- "Apache-2.0": Cho phép sử dụng tự do nhưng phải giữ nguyên giấy phép.
- "GPL-3.0": Yêu cầu mở mã nguồn nếu phân phối lại.
- "ISC": Giấy phép đơn giản, tương tự MIT.

### Chạy tương tự như câu a để hoàn thành bài tập

### e. <span style="color: red;">Add a Version to Your package.json (Thêm Phiên bản vào package.json của bạn)</span>
### `Version` trong tệp package.json là trường bắt buộc dùng để chỉ định phiên bản hiện tại của dự án.
Phiên bản có định dạng x.y.z theo Semantic Versioning:
- x (Major): Thay đổi lớn, không tương thích ngược.
- y (Minor): Thêm tính năng mới, vẫn tương thích.
- z (Patch): Sửa lỗi nhỏ, không thay đổi tính năng.
Giúp quản lý và phát hành các phiên bản dự án một cách rõ ràng. ✅


### f. <span style="color: red;">Expand Your Project with External Packages from npm (Mở rộng dự án của bạn với các gói bên ngoài từ npm)</span>
### `Dependencies` field trong tệp `package.json` là nơi liệt kê các thư viện (package) mà dự án của bạn phụ thuộc vào.

### g. <span style="color: red;">Manage npm Dependencies By Understanding Semantic Versioning (Quản lý các phụ thuộc npm bằng cách hiểu về phiên bản ngữ nghĩa)</span>
### `Semantic Versioning (SemVer)` là một cách đặt tên phiên bản phần mềm để giúp dễ dàng quản lý và theo dõi các thay đổi trong dự án.
### Cú pháp của nó là: `MAJOR.MINOR.PATCH`, ví dụ: `1.2.13`.
### 🌟 Tóm tắt dễ hiểu:
- MAJOR: Thay đổi lớn, phá vỡ code cũ (cẩn thận khi cập nhật!).
- MINOR: Thêm tính năng mới, vẫn chạy được với code cũ.
- PATCH: Sửa lỗi nhỏ, không ảnh hưởng code cũ.
### 🚀 Ví dụ trong thực tế:
Giả sử bạn đang dùng một thư viện có phiên bản 2.5.3:
- Nếu nó cập nhật lên 3.0.0 → Thay đổi lớn, có thể làm hỏng code cũ.
- Nếu nó cập nhật lên 2.6.0 → Thêm tính năng mới, vẫn tương thích với code cũ.
- Nếu nó cập nhật lên 2.5.4 → Sửa lỗi nhỏ, không ảnh hưởng gì.

### h. <span style="color: red;">Use the Tilde-Character to Always Use the Latest Patch Version of a Dependency (Sử dụng ký tự Tilde để luôn sử dụng phiên bản vá lỗi mới nhất của một phụ thuộc)</span>
### Ký tự ``~ (tilde)`` trong phiên bản của npm được dùng để cho phép cập nhật lên phiên bản `PATCH` mới nhất trong cùng một phiên bản `MAJOR.MINOR`.
### 🚀 Cách hoạt động của ``~``:
Khi bạn viết ~1.2.13, npm sẽ:
- Cập nhật lên bất kỳ phiên bản nào có dạng 1.2.x, miễn là MAJOR và MINOR không đổi.
- Ví dụ: Các phiên bản hợp lệ là 1.2.14, 1.2.15, 1.2.99, ...
- Không cập nhật lên 1.3.0 (vì đã thay đổi MINOR).
### ✅ Tóm lại:
- Ký tự ~ giúp cập nhật tự động lên phiên bản PATCH mới nhất.
- Rất hữu ích khi bạn muốn đảm bảo không thay đổi phiên bản chính (MAJOR) và phụ (MINOR) để tránh lỗi tương thích.
- Nếu bạn muốn cập nhật lên bất kỳ phiên bản MINOR nào mới nhất, hãy dùng ^ thay vì ~.

### i. <span style="color: red;">Use the Caret-Character to Use the Latest Minor Version of a Dependency (Sử dụng ký tự dấu mũ để sử dụng phiên bản phụ thuộc mới nhất)</span>
### Ký tự ``^ (caret)`` trong phiên bản của npm được dùng để cho phép cập nhật lên phiên bản `MINOR` mới nhất, miễn là phiên bản `MAJOR` không đổi.
### 🚀 Cách hoạt động của ``^``:
Khi bạn viết ^1.2.13, npm sẽ:
- Cập nhật lên bất kỳ phiên bản nào có dạng 1.x.x, miễn là MAJOR (1) không đổi.
- Ví dụ: Các phiên bản hợp lệ là 1.3.0, 1.9.15, 1.99.99, ...
- Không cập nhật lên 2.0.0 (vì đã thay đổi MAJOR).
### ✅ Tóm lại:
- ^ giúp đảm bảo bạn nhận được các cập nhật nhỏ và sửa lỗi mới nhất, nhưng không làm thay đổi các thay đổi lớn (MAJOR).
- Hữu ích khi bạn muốn nhận các tính năng mới mà không lo làm hỏng tương thích với phiên bản hiện tại.
### j. <span style="color: red;">Remove a Package from Your Dependencies (Xóa một gói khỏi các phụ thuộc của bạn)</span>
### Xóa dòng này theo đề bài:

## 2. <span style="color: brown;">Basic Node and Express (Cơ bản về Node và Express)</span>
### a. <span style="color: red;">Meet the Node console (Gặp gỡ console của Node)</span>

### File `myApp.js` là tệp JavaScript chính trong dự án `Node.js` của bạn. Đây là nơi bạn viết mã để tạo máy chủ, xử lý yêu cầu ``(request)``, gửi phản hồi ``(response)`` và thực hiện các logic khác. Trong bài tập mà bạn đang làm, đề bài yêu cầu bạn thêm một dòng lệnh vào tệp `myApp.js` để in ra dòng chữ ``"Hello World"`` bằng cách sử dụng `console.log()`.
### b. <span style="color: red;">Start a Working Express Server (Bắt đầu một máy chủ Express đang hoạt động)</span>

### Đề bài yêu cầu bạn tạo một máy chủ Express đơn giản để phản hồi khi có yêu cầu (request) từ phía người dùng.
### Cụ thể:
#### Khởi tạo máy chủ Express
- Dự án của bạn đã có sẵn file `myApp.js`, trong đó đã khởi tạo một ứng dụng Express.
#### Tạo một route để phản hồi yêu cầu GET
- Bạn cần sử dụng phương thức `app.get()` để định nghĩa route:
### `app.get(PATH, HANDLER);`
- `PATH`: Đường dẫn (trong bài là / - root).
- `HANDLER`: Hàm xử lý yêu cầu (dạng function(req, res) {...}).
#### Gửi phản hồi (response)
- Sử dụng `res.send()` để gửi chuỗi "Hello Express" về phía người dùng.
### c. <span style="color: red;">Serve an HTML File (Phục vụ một tập tin HTML)</span>

### Đề bài muốn chúng ta sử dụng phương thức `res.sendFile()` trong một tuyến đường `(route) GET` để phục vụ tệp HTML có đường dẫn là `/views/index.html`.
### Phương thức `res.sendFile(path)`:
- Gửi một tệp `HTML` từ máy chủ đến trình duyệt của người dùng.
- Thiết lập các tiêu đề `HTTP` phù hợp để trình duyệt biết cách xử lý tệp `(như HTML, CSS, JS)`.
- Đọc tệp từ ổ đĩa và gửi nó dưới dạng phản hồi.
- Đường dẫn phải là tuyệt đối (không phải tương đối).
### Trong `Node.js`, bạn có thể lấy đường dẫn tuyệt đối bằng cách sử dụng biến toàn cục:
- `__dirname`: Trả về đường dẫn thư mục hiện tại của tệp đang chạy.
- `+ '/relativePath/file.ext'`: Nối thêm đường dẫn tương đối vào thư mục hiện tại.
### d. <span style="color: red;">Serve Static Assets (Phục vụ tài sản tĩnh)</span>

### Đề bài yêu cầu chúng ta thực hiện các công việc sau:
Phục vụ các tài nguyên tĩnh ``(Serve Static Assets)`` như `CSS, hình ảnh, và JavaScript` từ một thư mục.
- Ví dụ: `style.css, hình ảnh, tệp JavaScript, v.v`.
Sử dụng `middleware express.static()` của `Express` để làm điều này.
- `Middleware` này sẽ cho phép `Express` tự động phục vụ các tệp trong một thư mục cụ thể mà không cần định nghĩa nhiều tuyến đường.
### Sử dụng phương thức app.use() để thiết lập middleware phục vụ tệp tĩnh.
Cú pháp:
### `app.use('/public', express.static(__dirname + '/public'));`
Ở đây:
- ``'/public'`` là đường dẫn mà người dùng sẽ truy cập để lấy tài nguyên.
- `express.static(__dirname + '/public')` là middleware phục vụ các tệp trong thư mục `public`.
### e. <span style="color: red;">Serve JSON on a Specific Route (Phục vụ JSON trên một tuyến đường cụ thể)</span>

### Đề bài yêu cầu tạo một `API` sử dụng phương thức `GET` với đường dẫn là `/json`.
Bài toán yêu cầu chúng ta tạo một REST API đơn giản, với các tiêu chí sau:
- Tạo một route /json trong Express.
- Xử lý yêu cầu GET đến /json.
- Phản hồi một đối tượng JSON { "message": "Hello json" }.
### Chi tiết về từng thành phần:
`app.get('/json', callback)`:
- Định nghĩa route /json, xử lý yêu cầu GET.
`req`: Đại diện cho yêu cầu (request) từ client.
`res`: Đại diện cho phản hồi (response) từ server.
`res.json({ message: "Hello json" })`:
- Gửi phản hồi JSON ``{ "message": "Hello json" }`` về client.
- `res.json()` tự động thiết lập `Content-Type: application/json`, giúp trình duyệt hiểu đây là dữ liệu JSON.
### f. <span style="color: red;">Use the .env File (Sử dụng tệp .env)</span>
### 1. Tóm tắt yêu cầu
- Tạo file `.env` để lưu biến môi trường `MESSAGE_STYLE=uppercase`.
- Đọc biến môi trường trong Express.js bằng `process.env.MESSAGE_STYLE`.
- Nếu `MESSAGE_STYLE=uppercase`, trả về JSON với chữ in hoa.
- Nếu không, trả về JSON với chữ thường.
- Sử dụng thư viện `dotenv` để load biến môi trường.
### 2. Hướng dẫn thực hiện yêu cầu trên
#### - Bước 1: Tạo file .env và thêm dòng sau vào: `MESSAGE_STYLE=uppercase`

- Việc tạo `file .env` là để lưu biến môi trường, giúp cấu hình ứng dụng mà không cần sửa mã nguồn.
- Cụ thể trong trường hợp này, .env giúp bạn:
✅ Thay đổi hành vi của ứng dụng mà không cần sửa code (chỉ cần chỉnh `MESSAGE_STYLE`).
✅ Bảo mật thông tin như `API key`, mật khẩu cơ sở dữ liệu (tránh lưu trực tiếp trong code).
✅ Dễ dàng tùy chỉnh khi triển khai trên các môi trường khác nhau (`dev, test, production`).
- Trong bài này, `file .env` lưu biến `MESSAGE_STYLE=uppercase`, giúp server biết có cần chuyển chữ hoa "HELLO JSON" hay không.
#### - Bước 2: Sửa file `myApp.js`

1. Thêm đoạn sau vào đầu file myApp.js để nạp biến môi trường: `require('dotenv').config();`
- Mục đích: Nạp các biến môi trường từ `file .env` vào `process.env`, giúp ứng dụng có thể truy cập chúng.
Hoạt động: Khi chạy, `dotenv.config()` đọc nội dung `file .env`, rồi gán các biến trong đó vào `process.env`.
2. Sau đó, sửa code của `route /json`
- Giải thích:
`process.env.MESSAGE_STYLE` đọc biến từ `.env`.
Nếu giá trị là ``"uppercase"``, chuyển message thành chữ hoa ``(toUpperCase())``.
### g. <span style="color: red;">Implement a Root-Level Request Logger Middleware (Triển khai một Middleware ghi nhật ký yêu cầu cấp gốc)</span>

🔹 `app.use((req, res, next) => { ... })`
- `app.use(...)` là cách để đăng ký một `middleware` trong `Express`.
- `Middleware` này sẽ chạy cho mọi `request` đến server, bất kể method (`GET, POST, PUT, DELETE, v.v.`) hay đường dẫn nào.
🔹 `req.method`
- `req.method` lấy `HTTP method` của request.
Ví dụ:
- Khi bạn gửi `GET request` đến /json, req.method sẽ là "GET".
- Khi bạn gửi `POST request` đến /data, req.method sẽ là "POST".
🔹 `req.path`
- `req.path` lấy đường dẫn của request (`path của URL`).
Ví dụ:
- Nếu truy cập http://localhost:3000/json, thì req.path là "/json".
- Nếu truy cập http://localhost:3000/user/123, thì req.path là "/user/123".
🔹 `req.ip`
- `req.ip` lấy địa chỉ `IP` của client gửi request đến server.
- Khi bạn chạy local trên máy tính của mình, IP thường sẽ là ::ffff:127.0.0.1.
- Khi deploy server lên internet, IP này có thể là của user thật.
🔹 `console.log(...)`
- Mục đích là in ra thông tin của request lên terminal, giúp ghi log để debug hoặc giám sát.
- Dùng Template String (`${}`) để tạo chuỗi có biến động.
- Chuỗi in ra có dạng: `GET` `/json` - `::ffff:127.0.0.1`
🔹 `next()`
- Quan trọng! Nếu không gọi `next()`, request sẽ bị mắc kẹt ở `middleware` này và không đi tiếp đến các route khác.
- `next()` báo cho Express biết rằng `middleware` đã xử lý xong và có thể tiếp tục xử lý request bằng `middleware` hoặc route tiếp theo.
Ví dụ: nếu không có `next()`, khi bạn vào /json, trình duyệt sẽ chờ mãi mà không nhận được phản hồi.
### `Middleware` này ghi log tất cả `request` đến `server`, hiển thị thông tin `method, path, IP` trên terminal. Nó không chặn `request`, chỉ log rồi gọi `next()` để tiếp tục xử lý. 🚀
### h. <span style="color: red;">Chain Middleware to Create a Time Server (Nối Middleware để tạo Time Server)</span>
Để tóm tắt cách làm bài này:
1. Định nghĩa `route` với `middleware`: Sử dụng `app.get("/now", ...)` để định nghĩa route cho yêu cầu `GET` tới `/now`.
2. Middleware đầu tiên:
- Thêm thời gian hiện tại vào đối tượng req bằng `req.time = new Date().toString()`.
- Gọi `next()` để chuyển quyền điều khiển sang `middleware` tiếp theo.
3. Route handler (middleware thứ hai):
- Trả về thời gian (được lưu trong req.time) dưới dạng JSON với `res.json({ time: req.time })`.
Cách thức hoạt động:
- Khi có yêu cầu GET tới /now, middleware đầu tiên thêm thời gian vào req.
- Sau đó, route handler trả về thời gian trong định dạng JSON.
Kết quả:
- Yêu cầu GET tới /now sẽ trả về phản hồi JSON với thời gian hiện tại, ví dụ:
``{
"time": "Wed Apr 03 2025 13:00:00 GMT+0000 (UTC)"
}``

### i. <span style="color: red;">Get Route Parameter Input from the Client (Nhận thông tin đầu vào tham số tuyến đường từ máy khách)</span>
- Bước 1: Định nghĩa `route` với phương thức `GET`: `app.get("/:word/echo", ...)`:
-Tạo một route động trong Express, cho phép xử lý các yêu cầu HTTP GET đến các đường dẫn có định dạng giống ``/:word/echo``.
-``:word`` là tham số động (`route parameter`), cho phép người dùng thay đổi phần này theo ý muốn trong `URL`.
-Giúp tạo ra các `API` linh hoạt, ví dụ: ``/cat/echo, /banana/echo, ...``
-Cho phép server thu thập dữ liệu từ chính đường dẫn URL, không cần query string hay body.
- Bước 2: Viết hàm callback xử lý `request` và gửi `response`:
-Đây là một hàm `callback` xử lý `request` và phản hồi lại `client`.
-`req` chứa thông tin từ phía `client` gửi đến (trong đó có `req.params` để lấy `route parameter`).
-`res` dùng để gửi dữ liệu phản hồi lại `client` dưới dạng `JSON`.
-Khi client gửi yêu cầu như `GET /hello/echo`, thì `req.params.word` sẽ chứa ``"hello"``.
-Server sẽ gửi lại phản hồi dạng JSON: `{ "echo": "hello" }`

- Tác dụng của bài này: Giúp bạn biết cách lấy dữ liệu từ đường dẫn (URL) bằng route parameter trong Express và gửi lại phản hồi dưới dạng JSON.

### j. <span style="color: red;">Get Query Parameter Input from the Client (Nhận tham số truy vấn đầu vào từ máy khách)</span>

- Server lấy giá trị `first`, `last` từ `req.query`
- `${...} ${...}` Nối 2 chuỗi thành họ tên đầy đủ

### k. <span style="color: red;">Use body-parser to Parse POST Requests (Sử dụng body-parser để phân tích các yêu cầu POST)</span>
- ``let bodyParser = require('body-parser');`` : Nạp thư viện để xử lý dữ liệu POST:
-`bodyParser`: là cái dụng cụ (thư viện) giúp mổ xẻ dữ liệu trong body (phần ẩn trong request khi người dùng gửi form).
-``require('body-parser')``: nghĩa là “mượn cái dụng cụ body-parser từ thư viện có sẵn”.
- ``app.use(bodyParser.urlencoded({ extended: false }));`` : Cho phép Express hiểu và xử lý dữ liệu từ form HTML gửi bằng POST:
-``bodyParser.urlencoded(...)``: là cách bạn chọn chế độ xử lý dữ liệu kiểu form HTML (gửi bằng POST).
-``{ extended: false }``: tạm hiểu là: “Dữ liệu đơn giản thôi, chỉ là chữ hoặc mảng, không cần phức tạp quá.”

### l. <span style="color: red;">Get Data from POST Requests (Lấy dữ liệu từ các yêu cầu POST)</span>
- Xử lý dữ liệu khi người dùng gửi form bằng phương thức POST tới đường dẫn /name, và trả lại dữ liệu dưới dạng JSON như sau: `{ "name": "firstname lastname" }`


- Ứng dụng thực tế:
-Dùng trong đăng ký tài khoản, gửi thông tin liên hệ, tạo bài viết mới, v.v...
-Làm nền tảng cho các hệ thống gửi dữ liệu từ client đến server.
## 3. <span style="color: brown;">MongoDB and Mongoose (MongoDB và Mongoose)</span>

### a. <span style="color: red;">Install and Set Up Mongoose (Cài đặt và thiết lập Mongoose)</span>

### b. <span style="color: red;">Create a Model (Tạo một mô hình)</span>

- `Bước 1`: Nạp biến môi trường: `require('dotenv').config();`
-Sử dụng gói `dotenv` để nạp các biến môi trường từ file ``.env`` (đặc biệt là biến `MONGO_URI` chứa đường dẫn kết nối MongoDB).
-Điều này giúp bảo mật thông tin nhạy cảm như `username/password` hoặc `URI`.
- `Bước 2`: Nhúng thư viện Mongoose: `const mongoose = require("mongoose");`
-Gọi thư viện `mongoose` – đây là ODM (Object Data Modeling) giúp thao tác với `MongoDB` dễ dàng bằng cách dùng cú pháp hướng đối tượng.
- `Bước 3`: Lấy Schema constructor từ mongoose: `const { Schema } = mongoose;`
-Giải nén Schema constructor từ Mongoose, dùng để định nghĩa cấu trúc tài liệu (document).
-Câu này là cách `viết tắt` để lấy đối tượng `Schema` ra từ `mongoose`, giúp bạn viết code gọn hơn sau này.
-Ví dụ: `const personSchema = new Schema({ ... });`
thay vì: `const personSchema = new mongoose.Schema({ ... });`
- `Bước 4`: Kết nối tới cơ sở dữ liệu MongoDB: `mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });`
-`Kết nối` tới `MongoDB` sử dụng `URI` được lưu trong biến môi trường.
-`useNewUrlParser` và `useUnifiedTopology` là các tùy chọn để sử dụng các chức năng mới và ổn định hơn.
- `Bước 5`: Tạo Schema cho "Person":
```js
const personSchema = new Schema({
name: { type: String, required: true },
age: Number,
favoriteFoods: [String]
});
```
Tạo một schema tên personSchema với cấu trúc như sau:
-`name`: kiểu `String`, bắt buộc phải có (validator required: `true`)
-`age`: kiểu `Number`, không bắt buộc
-`favoriteFoods`: một mảng các chuỗi (``[String]``) đại diện cho các món ăn yêu thích
- `Bước 6`: Tạo model từ schema: `const Person = mongoose.model("Person", personSchema);`
Model này cho phép bạn thực hiện các thao tác CRUD như:
-``Person.create(...)``
-``Person.find(...)``
-``Person.findByIdAndUpdate(...)``
-``Person.deleteOne(...)``

### c. <span style="color: red;">Create and Save a Record of a Model (Tạo và Lưu Bản Ghi của Mô Hình)</span>

- `Bước 1`: Định nghĩa hàm createAndSavePerson:
`const createAndSavePerson = (done) => {`
-Thực hiện tạo người mới + lưu vào `MongoDB`
-Khi hoàn tất, gọi hàm ``done()`` để báo cho `FreeCodeCamp` biết kết quả.
- `Bước 2`: Tạo một đối tượng người mới
```js
const person = new Person({
name: "Phong",
age: 19,
favoriteFoods: ["pizza", "icecream"]
});
```
-`name`: phải là chuỗi (`string`) và bắt buộc (do `required: true` trong `schema`)
-`age`: số (`number`), có thể có hoặc không
-`favoriteFoods`: là một mảng các chuỗi, ví dụ ``["pizza", "icecream"]``
🔸 ``Dùng new Person({...}) để tạo ra một document mới, nhưng chưa lưu vào database đâu nhé! (mới chỉ nằm trong bộ nhớ máy)``
- `Bước 3`: Lưu document đó vào MongoDB: ` person.save(function(err, data) {`
Đây là phương thức .save() dùng để lưu document vào MongoDB.
🔸 .save() là phương thức bất đồng bộ (asynchronous), nghĩa là:
-Bạn không biết chắc lúc nào nó xong
-Nên bạn phải cung cấp cho nó một `callback function` để xử lý kết quả
🔸 Hàm callback nhận 2 đối số:
-`err`: nếu có lỗi trong khi lưu (ví dụ thiếu name, hoặc MongoDB bị tắt)
-`data`: dữ liệu đã lưu thành công từ MongoDB
- `Bước 4`: Kết thúc: Gọi done(err, data); để báo kết quả
```js
done(err, data);
});
};
```
-Dù thành công hay thất bại, bạn đều gọi ``done(...)`` để `FreeCodeCamp` biết được kết quả bạn làm ra.

### d. <span style="color: red;">Create Many Records with model.create() (Tạo nhiều bản ghi với model.create())</span>
- `Bước 1`: ``const createManyPeople = (...) => ...``
-Đây là cách khai báo một hàm mũi tên (`arrow function`).
-Hàm này tên là `createManyPeople`, được dùng để tạo nhiều bản ghi (`records`) người dùng trong cơ sở dữ liệu `MongoDB`.
-Nó nhận vào 2 tham số:
+`arrayOfPeople` Một mảng chứa nhiều đối tượng `Person`, mỗi đối tượng mô tả một người.
+`done` Một `callback function` sẽ được gọi sau khi ghi dữ liệu xong hoặc khi có lỗi xảy ra.
- `Bước 2`: ``Person.create(arrayOfPeople, done);``
-`Person` là `Model` được tạo từ `Schema` ở `câu b`
-``create()`` là hàm có sẵn của `Mongoose`, dùng để tạo mới và lưu nhiều tài liệu (`documents`) vào `MongoDB`.
-Tham số `arrayOfPeople`: Là mảng gồm các đối tượng
-Tham số `done`: là `callback` sẽ được gọi với 2 tham số:
+`err`: Nếu có lỗi khi ghi vào DB.
+`data`: Là mảng các bản ghi vừa được tạo.
-Giống như `câu c`, trong bài này, ta tạo nhiều người một lúc bằng cách đưa vào một mảng.

### e. <span style="color: red;">Use model.find() to Search Your Database (Sử dụng model.find() để tìm kiếm cơ sở dữ liệu của bạn)</span>
📘 Bài 4: Dùng ``Model.find()`` để tìm nhiều tài liệu theo điều kiện: Sử dụng phương thức ``Model.find()`` để tìm tất cả những người có tên giống với `personName` được truyền vào.
- Bước 1: Định nghĩa hàm `findPeopleByName`
`const findPeopleByName = (personName, done) => {`
-Đây là một hàm xử lý (`callback`) được yêu cầu bởi `FreeCodeCamp`.
-Tham số `personName`: là tên người dùng cần tìm (``VD: "Mary"``).
-Tham số `done`: là một `callback` để trả kết quả về cho hệ thống kiểm thử (`testing framework` của `FreeCodeCamp`).
- Bước 2: Gọi ``Person.find()``
` Person.find({ name: personName }, (err, data) => {`
-`Person`: là `Mongoose Model` đại diện cho collection people trong MongoDB.
-``.find({ name: personName })``: là một `truy vấn` MongoDB:
-Trả về mảng các document có trường `name` bằng `personName`.
-Ví dụ: Nếu gọi ``findPeopleByName("Mary")``, thì nó sẽ tìm trong collection những người có `name` là ``"Mary"``.
-``(err, data) => {...}`` là callback:
-`err`: nếu có lỗi, nó chứa lỗi.
-`data`: là kết quả trả về, một mảng các đối tượng người (Person) có tên đúng.
- Bước 3: Trả kết quả với ``done()``

### f. <span style="color: red;">Use model.findOne() to Return a Single Matching Document from Your Database (Sử dụng model.findOne() để trả về một tài liệu khớp duy nhất từ cơ sở dữ liệu của bạn)</span>
- Dùng ``Model.findOne()`` để tìm một tài liệu khớp duy nhất
- Bước 1: Khai báo hàm
`const findOneByFood = (food, done) => {`
-Hàm `findOneByFood` nhận:
-`food`: là tên món ăn (kiểu chuỗi), ví dụ "pizza".
-`done`: là callback để báo kết quả cho FreeCodeCamp.
- Bước 2: Truy vấn bằng ``findOne()``
`Person.findOne({ favoriteFoods: food }, (err, data) => {`
-`Person`: là Mongoose `model`, ánh xạ với collection people trong MongoDB.
-``.findOne({ favoriteFoods: food })``: là truy vấn tìm 1 người đầu tiên có favoriteFoods chứa food (ví dụ "pizza").
Lưu ý:
-favoriteFoods là mảng nên ta dùng ``{ favoriteFoods: food }`` để tìm xem mảng đó có chứa giá trị food.
- 💡 Kết luận nhanh :
- Dùng ``.find()`` → khi bạn cần nhiều kết quả → trả về mảng
- Dùng ``.findOne()`` → khi bạn cần đúng 1 kết quả → trả về object

### g. <span style="color: red;">Use model.findById() to Search Your Database By _id (Sử dụng model.findById() để Tìm kiếm Cơ sở dữ liệu của bạn Theo _id)</span>
- `Bước 1`: Định nghĩa hàm `findPersonById`
Hàm này sẽ nhận vào hai tham số:
-`personId`: ID của người cần tìm.
-`done`: callback để xử lý kết quả trả về từ truy vấn.
```js
const findPersonById = (personId, done) => {
// Bước 2: Sử dụng phương thức `findById()` của Mongoose
Person.findById(personId, (err, data) => {
// Bước 3: Gọi callback với lỗi và dữ liệu trả về
done(err, data);
});
};
```

`Bài này giúp tìm kiếm tài liệu trong MongoDB bằng _id thông qua phương thức findById() của Mongoose, giúp truy xuất dữ liệu nhanh chóng và hiệu quả.`
### h. <span style="color: red;">Perform Classic Updates by Running Find, Edit, then Save (Thực hiện Cập nhật Cổ điển bằng cách Chạy Tìm, Chỉnh sửa, sau đó Lưu)</span>
- `TÓM TẮT ĐỀ BÀI`: Tìm một người bằng `_id`, thêm ``"hamburger"`` vào mảng `favoriteFoods`, sau đó lưu lại tài liệu đã chỉnh sửa bằng ``save()``. Nếu `favoriteFoods` không có kiểu rõ ràng trong `Schema`, cần dùng ``markModified()`` trước khi lưu.
`Bước 1: Khai báo món ăn cần thêm`
- Dùng: ``const foodToAdd = "hamburger"``
- Mục đích: Định nghĩa chuỗi cần thêm vào favoriteFoods
`Bước 2: Tìm kiếm`
- Dùng: ``Person.findById(personId, callback)``
- Mục đích: Tìm một người theo _id trong cơ sở dữ liệu
`Bước 3: Chỉnh sửa`
- Dùng: ``data.favoriteFoods.push(foodToAdd)``
- Mục đích: Thêm "hamburger" vào mảng favoriteFoods
`Bước 4: Lưu lại`
- Dùng: ``data.save((err, newData) => { ... })``
- Mục đích: Lưu lại tài liệu đã chỉnh sửa vào database
`Bước 5: Trả kết quả`
- Dùng: ``done(err, newData)``
- Mục đích: Trả về tài liệu đã cập nhật hoặc lỗi nếu có

### i. <span style="color: red;">Perform New Updates on a Document Using model.findOneAndUpdate() (Thực hiện cập nhật mới trên tài liệu bằng cách sử dụng model.findOneAndUpdate())</span>
- `TÓM TẮT ĐỀ BÀI`: Sử dụng ``findOneAndUpdate()`` trong `Mongoose` để tìm một người theo tên và cập nhật trường `age` thành `20`. Cần thêm ``{ new: true }`` để trả về tài liệu đã cập nhật.
`Bước 1: Xác định giá trị cần cập nhật`
- Dùng: `const ageToSet = 20`
- Mục đích: Định nghĩa giá trị độ tuổi cần cập nhật cho người tìm được.
`Bước 2: Tìm và cập nhật tài liệu`
- Dùng: ``Person.findOneAndUpdate(condition, update, options, callback)``
+ condition: { name: personName } → tìm tài liệu theo tên của người cần cập nhật.
+ update: { age: ageToSet } → cập nhật trường `age` thành giá trị `20`.
+ options: { new: true } → trả về tài liệu đã được cập nhật, không phải tài liệu cũ.
`Bước 3: Trả kết quả`
- Dùng: ``done(err, updatedPerson)``
- Mục đích: Trả về tài liệu đã cập nhật hoặc lỗi nếu có.

### j. <span style="color: red;">Perform New Updates on a Document Using model.findOneAndUpdate() (Thực hiện cập nhật mới trên tài liệu bằng cách sử dụng model.findOneAndUpdate())</span>
- `TÓM TẮT ĐỀ BÀI`: Sử dụng phương thức ``findByIdAndRemove()`` hoặc ``findOneAndRemove()`` để xóa một tài liệu trong `MongoDB` dựa trên `_id`. Phương thức này sẽ trả lại tài liệu đã bị xóa và đảm bảo tài liệu được xóa khỏi cơ sở dữ liệu.
`Bước 1: Tìm và xóa tài liệu`
- Dùng: ``Person.findByIdAndRemove(personId, callback)``
- Mục đích: Tìm tài liệu theo _id và xóa nó khỏi cơ sở dữ liệu.
`Bước 2: Trả kết quả`
- Dùng: ``done(err, deletedPerson)``
- Mục đích: Trả về tài liệu đã bị xóa hoặc lỗi nếu có.

### k. <span style="color: red;">Delete Many Documents with model.remove() (Xóa nhiều tài liệu với model.remove())</span>
- Mục tiêu:
Sử dụng phương thức ``Model.remove()`` trong `Mongoose` để xóa nhiều tài liệu thỏa mãn điều kiện, cụ thể là xóa tất cả những người có ``name === "Mary"``.
- Lưu ý:
Phương thức ``remove()`` không trả về các tài liệu đã bị xóa, mà trả về một `JSON` kết quả (số lượng bị ảnh hưởng, trạng thái thành công, v.v.). Phải truyền kết quả đó vào ``done()``.
`Bước 1: Xác định điều kiện xóa`
- Dùng: ``const nameToRemove = "Mary"``
- Mục đích: Tên những người cần bị xóa khỏi database
`Bước 2: Gọi phương thức xóa`
- Dùng: ``Person.remove({ name: nameToRemove }, callback)``
- Mục đích: Xóa tất cả tài liệu có name là "Mary"
- Kết quả trả về là một JSON (vd: { acknowledged: true, deletedCount: 3 })
`Bước 3: Trả kết quả cho hệ thống kiểm tra`
- Dùng: ``done(err, data)``
- Mục đích: Trả lại kết quả xóa (số lượng bị xóa, lỗi nếu có) cho hệ thống test hoặc xử lý tiếp

### l. <span style="color: red;">Chain Search Query Helpers to Narrow Search Results (Trợ lý truy vấn tìm kiếm chuỗi để thu hẹp kết quả tìm kiếm)</span>
- `TÓM TẮT ĐỀ BÀI`:
- Tìm những người yêu thích món ăn cụ thể (được lưu trong foodToSearch), sau đó:
+ Sắp xếp kết quả theo tên (`name`) tăng dần.
+ Giới hạn kết quả còn `2` người đầu tiên.
+ Ẩn trường `age` trong kết quả.
+ Dùng ``.exec()`` để thực thi truy vấn, truyền ``done(err, data)`` vào.
``Bước 1: Tìm người có món ăn yêu thích là "burrito"``
- Dùng: Person.find({ favoriteFoods: foodToSearch })
``Bước 2: Sắp xếp kết quả theo tên (tăng dần)``
- Dùng: .sort({ name: 1 })
`Bước 3: Giới hạn kết quả trả về chỉ 2 người`
- Dùng: .limit(2)
`Bước 4: Ẩn trường 'age' khỏi kết quả`
- Dùng: .select("-age")
`Bước 5: Thực thi truy vấn và xử lý kết quả`
- Dùng: .exec((err, data) => done(err, data))

## 4. <span style="color: brown;">Back End Development and APIs Projects (Dự án phát triển Back End và API)</span>
### a. <span style="color: red;">Timestamp Microservice (Dịch vụ vi mô dấu thời gian)</span>
1. ``Unix timestamp (unix) là gì?``
- Unix timestamp là số milliseconds (hoặc giây, tùy hệ thống) tính từ thời điểm 00:00:00 ngày 1 tháng 1 năm 1970 (UTC). Thời điểm đó được gọi là Epoch time – là mốc thời gian gốc của máy tính.

2. ``UTC (Coordinated Universal Time) là gì?``
- UTC là tiêu chuẩn thời gian quốc tế, không phụ thuộc múi giờ nào. UTC luôn giống GMT (Greenwich Mean Time). Nó là thời gian chuẩn mà mọi múi giờ khác so sánh với (VD: Việt Nam là UTC+7)
### BƯỚC 1: Khởi tạo ứng dụng Express
```js
const express = require('express');
const app = express();
```
- ``require('express')``: nạp thư viện Express.
- ``app = express()``: tạo ứng dụng Express, là nền của web server. 👉 Tác dụng: Bắt đầu xây dựng một ứng dụng Node.js web server đơn giản.
### BƯỚC 2: Cung cấp file tĩnh từ thư mục `public`
```js
app.use(express.static('public'));
```
- ``express.static('public')``: `middleware` này cho phép Express phục vụ các file tĩnh (`CSS, JS, ảnh, v.v`) trong thư mục ``public/``.
- Ví dụ: nếu có `file public/style.css`, bạn có thể dùng ``<link rel="stylesheet" href="/style.css">`` trong HTML.
- 👉 Tác dụng: Cho phép người dùng truy cập trực tiếp các `file frontend` nếu có, giúp hiển thị giao diện đúng cách.
### BƯỚC 3: Route cho trang chính ``/``
```js
app.get('/', (req, res) => {
res.sendFile(__dirname + '/views/index.html');
});
```
- ``app.get('/')``: định nghĩa route cho đường dẫn gốc của web.
- ``res.sendFile(...)``: gửi file HTML để trình duyệt hiển thị.
- `__dirname`: là đường dẫn tới thư mục hiện tại — dùng để tìm đúng vị trí của file index.html.
- 👉 Tác dụng: Khi người dùng vào địa chỉ chính, họ sẽ thấy giao diện HTML, có thể là nơi mô tả API hoặc hướng dẫn sử dụng.
### BƯỚC 4: Định nghĩa route API chính ``/api/:date?``
```js
app.get('/api/:date?', (req, res) => {
```
- Tạo route có thể nhận một tham số tùy chọn ``:date?``.
- Ví dụ:
+ /api/2015-12-25
+ /api/1451001600000
+ /api/ (không có tham số)
- ``👉`` Tác dụng: Đây là nơi xử lý toàn bộ yêu cầu chính của bài `Timestamp Microservice`.
### BƯỚC 5: Lấy giá trị ngày từ URL
```js
let dateString = req.params.date;
```
- Lưu giá trị tham số `date` vào biến `dateString`.
- Nếu URL là ``/api/2015-12-25`` → dateString = ``"2015-12-25"``.
- ``👉`` Tác dụng: Chuẩn bị đầu vào cho bước xử lý ngày.
### BƯỚC 6: Trường hợp không có tham số :date (ví dụ ``/api``)
```js
if (!dateString) {
const now = new Date();
return res.json({ unix: now.getTime(), utc: now.toUTCString() });
}
```
- Nếu `dateString` là `undefined` (người dùng không truyền tham số).
- Tạo đối tượng Date hiện tại.
- Trả về JSON với:
+ unix: thời gian hiện tại dạng timestamp.
+ utc: thời gian hiện tại dạng chuỗi chuẩn GMT.
- ``👉`` Tác dụng: Đáp ứng yêu cầu 7 và 8 của đề bài — trả về thời gian hiện tại.
### BƯỚC 7: Xử lý khi chuỗi là một timestamp số (ví dụ: "`1451001600000`")
```js
if (!isNaN(dateString)) {
dateString = parseInt(dateString);
}
```
- Nếu `dateString` chỉ là số (không phải dạng ngày tháng), chuyển nó sang số nguyên.
- Điều này để ``new Date(...)`` hiểu đúng — vì nếu là chuỗi số, nó sẽ coi như `timestamp`.
- ``👉`` Tác dụng: Đảm bảo thời gian dạng timestamp được xử lý chính xác.
### BƯỚC 8: Tạo đối tượng Date từ dateString
```js
const date = new Date(dateString);
```
- Tạo một đối tượng Date từ biến `dateString`.
- Có thể là `chuỗi ngày` (2015-12-25) hoặc `số timestamp` (1451001600000).
- ``👉`` Tác dụng: Chuẩn bị dữ liệu để kiểm tra và phản hồi theo định dạng JSON.
### BƯỚC 9: Kiểm tra nếu ngày không hợp lệ
```js
if (date.toString() === "Invalid Date") {
return res.json({ error: "Invalid Date" });
}
```
- Nếu ``new Date(...)`` không tạo được ngày hợp lệ, nó sẽ trả về "`Invalid Date`".
- Trường hợp này phải trả lỗi như `đề bài yêu cầu`: { ``error: "Invalid Date"`` }.
- ``👉`` Tác dụng: Đáp ứng yêu cầu 6 — kiểm tra tính hợp lệ của đầu vào.
### BƯỚC 10: Trả về định dạng JSON hợp lệ
```js
res.json({
unix: date.getTime(),
utc: date.toUTCString()
});
```
- Trả JSON với hai định dạng:
+ unix: dạng timestamp (milliseconds).
+ utc: dạng chuỗi ngày quốc tế.
- Đảm bảo đúng yêu cầu đề bài.
- ``👉`` Tác dụng: Đáp ứng yêu cầu 2, 3, 4, 5 của đề bài — định dạng đầu ra chuẩn.
### BƯỚC 11: Xuất module app
```js
module.exports = app;
```
- Để `file server.js` có thể sử dụng app này và chạy `server`.
- ``👉`` Tác dụng: Kết nối giữa ứng dụng và phần khởi động `server` (nằm trong `server.js`).
### Ảnh minh họa:
