
Web Development Essentials 035.3: Khái niệm cơ bản về SQL
Tạo bảng SQLite, hiểu khóa chính và CHECK, dùng CRUD có WHERE và bind tham số từ Node.js; kiểm tra transaction rollback, mở lại dữ liệu và API driver.

Chạy Node.js LTS, phân biệt ES module và CommonJS, package.json và lockfile; cài package DEMO cục bộ, dùng npm ci và kiểm tra server HTTP trên loopback.
Một tệp JavaScript chạy được trong browser nhưng khi đưa sang terminal lại báo document không tồn tại. Một dự án khác hoạt động trên máy tác giả nhưng người học cài phụ thuộc ra phiên bản khác. Hai tình huống này cho thấy cần hiểu cả môi trường chạy lẫn cách mô tả dự án, trước khi viết server tiếp nhận yêu cầu thật.
Bài 035.1, Khái niệm cơ bản về NodeJS, tham chiếu giáo trình 030-100 trang in 290–303. Nội dung và lab được viết độc lập, đối chiếu nguồn ngày 23/10/2026. Bạn cần biết JavaScript, module, terminal và HTTP cơ bản. Sau bài này, bạn có thể chạy chương trình Node, dùng npm trong thư mục riêng, phân biệt manifest với lockfile và kiểm tra một HTTP server chỉ lắng nghe trên loopback.
Node.js là runtime JavaScript dùng được cho công cụ dòng lệnh, script và ứng dụng server, không chỉ web backend. Ngôn ngữ vẫn theo ECMAScript; API của host khác browser. Node cung cấp khả năng làm việc với tệp, tiến trình và mạng, còn document của một trang HTML không có sẵn như trong browser. Chạy cùng cú pháp không đồng nghĩa có cùng đối tượng toàn cục.
Node dùng V8 để thực thi JavaScript và libuv cùng các thành phần khác cho nhiều hoạt động của runtime. Mô tả “JavaScript chỉ được thông dịch” hoặc “mọi mã đều được biên dịch ngay thành machine code” đều quá đơn giản. Với người mới, cần biết runtime thực thi chương trình và quản lý các API host, chưa cần tối ưu theo chi tiết nội bộ engine.
Một luồng JavaScript chính thường xử lý callback của event loop trong ứng dụng thông thường. Nhiều thao tác I/O có thể tiến hành mà không giữ luồng đó chờ đồng bộ. Runtime và thư viện vẫn có thể sử dụng các luồng khác; Node cũng có worker threads. Vì vậy, “Node hoàn toàn chỉ có một thread” không phải mô tả chính xác toàn hệ thống.
Một phép tính CPU dài hoặc API đồng bộ chậm trên đường xử lý chính vẫn có thể trì hoãn các request khác. Đánh dấu hàm async không tự chuyển phép tính ấy sang worker. Chọn runtime và cách phân công công việc theo tải đo được; không kết luận Node không thể làm mọi tác vụ CPU hoặc mặc nhiên luôn tốt hơn các kiến trúc khác chỉ vì có event loop.
Ngày 23/10/2026, dòng Node 24 vẫn là LTS; bản vá 24.22.1 được công bố ngày 21/10. Node 26.12.1 cùng ngày được ghi Current. Loạt bài chọn dòng 24 LTS làm baseline phía server và ghi riêng phiên bản thật dùng trong từng lab. Khi bắt đầu học, chọn bản vá được hỗ trợ phù hợp, kiểm tra release notes và khả năng tương thích của phụ thuộc; không dùng bản thử nghiệm chỉ để có số lớn hơn.
Cài runtime theo hướng dẫn chính thức phù hợp hệ điều hành và chính sách máy. Bài này giả định đã có Node/npm trong môi trường lab; không chạy script cài đặt từ mạng bằng quyền quản trị. Nếu terminal tìm nhầm bản cũ, kiểm tra đường dẫn executable và shell đang dùng trước khi sửa mã ứng dụng.
node --version
npm --version
node -p "process.execPath"
Lệnh đầu ghi phiên bản Node, lệnh sau ghi phiên bản npm và executable thực tế. Npm đi cùng nhiều cách phân phối Node nhưng có phiên bản riêng. Lưu thông tin này trong ghi chú lab để phân biệt lỗi ngôn ngữ, runtime và công cụ. Không ghi toàn bộ biến môi trường vào log vì máy có thể chứa credential.
Gõ node không đối số để mở REPL, thử 2 + 3, rồi dùng .exit để thoát. Undefined sau một khai báo có thể là kết quả đánh giá của REPL, không phải thông báo thất bại. Với chương trình dài, lưu tệp để lần chạy sau có đầu vào rõ và có thể đưa vào Git.
Loạt bài dùng ES modules. Trong thư mục có package.json với "type": "module", tệp .js của gói được diễn giải theo chế độ module tương ứng. Đuôi .mjs cũng xác định ES module; .cjs xác định CommonJS. Hệ thống cũ thường có require và module.exports. Không ghép hai cách tùy ý rồi giả định mọi phiên bản Node đều suy luận như nhau.
Import với tiền tố node: thể hiện dùng module tích hợp như node:http. Import đường dẫn ./count.js lấy module của dự án. Import một tên package như express tìm theo cơ chế phân giải package. Việc một module tồn tại trong Node không có nghĩa browser có thể import nguyên tên đó mà không có cơ chế tương ứng.
Module giúp chia trách nhiệm, tái sử dụng và kiểm tra; hàm vẫn nằm trong module. Nó không phải lựa chọn thay thế hoàn toàn cho “dùng hàm”. Tránh thêm một dependency lớn để làm một thao tác nhỏ đã có API chuẩn phù hợp, nhưng cũng không tự viết lại mọi thư viện cần bảo trì.

Npm init tạo manifest package.json, không tự khởi tạo Git repository. Manifest mô tả tên, phiên bản, script, dependency và các metadata khác. Node_modules thường chứa mã các dependency đã cài; không đưa cả thư mục đó vào Git trong quy trình dự án thông thường. Với ứng dụng, lưu package.json và package-lock.json để người khác có căn cứ tái tạo cây dependency.
Package-lock ghi thông tin giải quyết cây phụ thuộc và integrity theo định dạng của npm, không phải bản sao dự phòng để sửa package.json hỏng. Npm install có thể thêm hoặc cập nhật dependency cùng lock. Npm ci dùng lock sẵn có, yêu cầu phù hợp manifest, xóa node_modules hiện tại và cài theo quy trình sạch; nó không dùng để thêm một package riêng.
Lệnh npm install ten-goi có thể tải mã và chạy lifecycle scripts theo cấu hình. Tên gần giống không bảo đảm cùng nhà phát hành. Trước khi thêm dependency, kiểm tra đúng tên, nguồn, giấy phép, phiên bản và nhu cầu sử dụng. Trong bài này, lab npm dùng package tự viết tại chỗ, tránh cần mã bên thứ ba chỉ để học cơ chế cài.
Ignore-scripts giảm việc tự chạy lifecycle scripts trong quá trình cài tương ứng; nó không biến mã dependency thành an toàn. Khi bạn import hoặc chạy script của dự án, mã vẫn được thực thi. Npm audit cũng có giới hạn phạm vi dữ liệu advisory, không chứng minh dự án không có lỗi bảo mật. Giữ việc đánh giá phụ thuộc theo bằng chứng cụ thể.
Tạo thư mục mới không chứa dữ liệu cá nhân. Chạy các lệnh sau tại thư mục đó. File package tự viết có thể đọc và kiểm tra toàn bộ trước khi cài. Không dùng sudo và không cài global.
npm init --yes
mkdir local-demo
Lưu local-demo/package.json và local-demo/index.js:
{"name":"lpi-local-demo","version":"1.0.0","type":"module","exports":"./index.js"}
export function countOpen(tickets) {
return tickets.filter(ticket => ticket.status === "open").length;
}
Đặt type thành chuỗi và private thành boolean bằng hai lệnh riêng. Tùy chọn json yêu cầu giá trị được phân tích như JSON, nên không dùng nó với chuỗi module chưa có dấu nháy JSON.
npm pkg set type=module
npm pkg set private=true --json
npm install ./local-demo --ignore-scripts --no-audit --no-fund
npm ci --ignore-scripts --no-audit --no-fund
Npm ci xóa và dựng lại node_modules, nên chỉ chạy trong thư mục lab đúng. Giữ local-demo trong cây dự án vì dependency file trỏ tới nó; lockfile không mang theo nội dung package cục bộ thay bạn.
Tạo app.js ở thư mục gốc:
import { countOpen } from "lpi-local-demo";
const tickets = [{ status: "open" }, { status: "closed" }, { status: "open" }];
console.log("OPEN", countOpen(tickets));
console.log("HOST", typeof document);
Chạy node app.js. Kỳ vọng OPEN 2 và HOST undefined. Kiểm tra manifest có dependency cục bộ, lockfile tồn tại và package được phân giải sau npm ci. Không sửa node_modules để tạo tính năng: mã nguồn local-demo mới là nơi cần sửa, rồi chạy lại quy trình thích hợp. Đưa node_modules vào .gitignore nếu khởi tạo Git sau này.
Lưu server.js cùng thư mục. Server không đọc tệp, không gọi hệ thống ngoài và chỉ trả dữ liệu DEMO. Dùng cổng 3000 nếu trống; nếu đã có dịch vụ khác, dừng bản lab hoặc chọn cổng khác thay vì kết thúc tiến trình không rõ chủ sở hữu.
import { createServer } from "node:http";
const server = createServer((request, response) => {
response.setHeader("Content-Type", "application/json; charset=utf-8");
if (request.method !== "GET") {
response.writeHead(405, { Allow: "GET" });
response.end(JSON.stringify({ error: "method" }));
return;
}
const url = new URL(request.url, "http://127.0.0.1");
if (url.pathname !== "/status") {
response.writeHead(404);
response.end(JSON.stringify({ error: "route" }));
return;
}
response.writeHead(200);
response.end(JSON.stringify({ service: "LPI-DEMO", ready: true }));
});
server.listen(3000, "127.0.0.1", () => console.log("DEMO listening on loopback:3000"));
Chạy node server.js, rồi dùng terminal khác kiểm tra GET /status, GET /missing và POST /status. Kỳ vọng lần lượt 200 với ready true, 404 và 405 có Allow GET. Đọc cả header lẫn body, không chỉ thấy một chuỗi JSON rồi kết luận thành công. Có thể dùng curl với -i, -X POST cho trường hợp phương thức sai. Dừng server bằng Ctrl+C khi kết thúc và xác nhận không còn phục vụ cổng lab.

Lab được chạy ngày 23/10/2026 bằng Node 24.22.0 và npm 11.19.1 trên container Alpine, user không phải root, filesystem chỉ đọc ngoài thư mục tạm và không có mạng ngoài. Package local được cài offline, npm ci giữ nguyên lock, hai log khớp. HTTP loopback trả đúng 200/404/405 và Allow; sau SIGINT cổng không còn phục vụ. Số cổng kiểm thử được cấp động để tránh xung đột, mã server giữ nguyên logic. Không nhận là đã thử bản vá 24.22.1, cài runtime, dependency bên thứ ba, TLS hay production.
Nếu import không thấy package, kiểm tra thư mục làm việc, package.json, đường dẫn file dependency và bước cài. Nếu gặp “require is not defined” trong ES module, xác định hệ module trước khi đổi tên biến. EADDRINUSE nghĩa địa chỉ/cổng đang được dùng; nó không chứng minh firewall chặn. Không bind 0.0.0.0 để sửa lỗi không liên quan.
Bạn hoàn thành khi giải thích được hai log app.js, tái tạo dependency bằng npm ci và phân biệt ba phản hồi HTTP. Server này chưa có TLS, authentication, giới hạn lưu lượng, quản lý tiến trình hay lưu dữ liệu, nên chỉ dùng cho lab loopback. Bài Express tiếp theo thêm routing và middleware có cấu trúc mà vẫn giữ các giới hạn kiểm tra rõ ràng.

Tạo bảng SQLite, hiểu khóa chính và CHECK, dùng CRUD có WHERE và bind tham số từ Node.js; kiểm tra transaction rollback, mở lại dữ liệu và API driver.

Tạo route Express, phục vụ CSS và render EJS; đọc query, params, body, kiểm tra dữ liệu form, escape HTML và phân biệt XSS với CSRF qua lab loopback.

Chọn node DOM, phân biệt collection sống và tĩnh, textContent và innerHTML; cập nhật class, thuộc tính và sự kiện nút bằng chuột, bàn phím trong lab DEMO.

Tổ chức hàm JavaScript với if, switch, vòng lặp và return; hiểu truthy, so sánh, break, continue và xử lý Promise bằng async/await qua dữ liệu DEMO.