Bạn đang tìm cách xây dựng một ứng dụng back-end mạnh mẽ và ổn định? Việc kết hợp sức mạnh của cơ sở dữ liệu quan hệ PostgreSQL với sự linh hoạt của Node.js trên một máy chủ Ubuntu 20.04 là một lựa chọn tuyệt vời. PostgreSQL nổi tiếng với tính toàn vẹn dữ liệu và khả năng mở rộng, trong khi Node.js cho phép xây dựng các ứng dụng nhanh chóng và hiệu quả.
Tuy nhiên, việc triển khai và kết nối hai công nghệ này đòi hỏi một quy trình cài đặt và cấu hình chính xác. Từ việc thiết lập PostgreSQL trên Ubuntu, tạo người dùng, cơ sở dữ liệu, cho đến viết mã kết nối trong Node.js, mỗi bước đều cần sự cẩn trọng để đảm bảo hệ thống hoạt động trơn tru và an toàn. Bài viết này sẽ hướng dẫn bạn chi tiết từng bước, giúp bạn tự tin xây dựng một nền tảng back-end vững chắc. Chúng ta sẽ cùng nhau đi qua quá trình cài đặt, cấu hình, thực hiện các thao tác cơ bản và xử lý các lỗi thường gặp.
Cài đặt PostgreSQL trên Ubuntu 20.04
Để bắt đầu, việc chuẩn bị một môi trường sạch và cập nhật là bước đầu tiên và quan trọng nhất. Điều này đảm bảo rằng bạn sẽ cài đặt được phiên bản PostgreSQL mới nhất và ổn định nhất, đồng thời tránh được các xung đột phần mềm không đáng có.
Chuẩn bị môi trường và cập nhật hệ thống
Trước khi cài đặt bất kỳ phần mềm mới nào, bạn nên kiểm tra phiên bản hệ điều hành và cập nhật danh sách các gói phần mềm của hệ thống. Thao tác này giúp đồng bộ hóa kho lưu trữ gói cục bộ với các nguồn từ xa, đảm bảo bạn nhận được các bản vá bảo mật và phiên bản phần mềm mới nhất.
Hãy mở terminal và chạy lệnh sau để xác minh phiên bản Ubuntu của bạn:
lsb_release -a
Kết quả sẽ hiển thị thông tin chi tiết về phiên bản, đảm bảo bạn đang làm việc trên Ubuntu 20.04. Tiếp theo, hãy cập nhật hệ thống bằng hai lệnh quen thuộc:
sudo apt update
sudo apt upgrade -y
Lệnh apt update sẽ làm mới danh sách các gói có sẵn, còn apt upgrade sẽ tiến hành nâng cấp tất cả các gói đã cài đặt lên phiên bản mới nhất. Quá trình này có thể mất vài phút tùy thuộc vào số lượng bản cập nhật.
Hướng dẫn cài đặt PostgreSQL từ repository chính thức
Sau khi hệ thống đã được cập nhật, bạn có thể tiến hành cài đặt PostgreSQL. Ubuntu 20.04 đã bao gồm một phiên bản ổn định của PostgreSQL trong kho lưu trữ mặc định, giúp việc cài đặt trở nên vô cùng đơn giản. Bạn chỉ cần chạy một lệnh duy nhất để cài đặt cả máy chủ PostgreSQL và gói contrib chứa các tiện ích bổ sung.
Thực thi lệnh sau trong terminal:
sudo apt install postgresql postgresql-contrib -y
Trình quản lý gói apt sẽ tự động tải xuống và cài đặt tất cả các gói phụ thuộc cần thiết. Sau khi quá trình cài đặt hoàn tất, dịch vụ PostgreSQL sẽ tự động khởi động.
Để chắc chắn rằng PostgreSQL đã được cài đặt và đang chạy đúng cách, bạn có thể kiểm tra trạng thái của dịch vụ bằng lệnh:
sudo systemctl status postgresql
Nếu dịch vụ đang hoạt động, bạn sẽ thấy một thông báo trạng thái active (exited) màu xanh lá. Điều này xác nhận rằng máy chủ cơ sở dữ liệu của bạn đã sẵn sàng để được cấu hình.

Cấu hình và tạo người dùng cơ sở dữ liệu PostgreSQL
Cài đặt thành công chỉ là bước khởi đầu. Để ứng dụng Node.js có thể tương tác với cơ sở dữ liệu, bạn cần cấu hình bảo mật ban đầu, tạo một người dùng riêng biệt và cấp cho người dùng đó các quyền hạn cần thiết.
Truy cập PostgreSQL shell và bảo mật ban đầu
Khi cài đặt, PostgreSQL tự động tạo một người dùng hệ thống tên là postgres. Đây là người dùng có quyền quản trị cao nhất, tương tự như root trong Linux. Để thực hiện các tác vụ quản trị, bạn cần chuyển sang người dùng này và truy cập vào giao diện dòng lệnh của PostgreSQL, hay còn gọi là psql.
Sử dụng lệnh sau để chuyển sang người dùng postgres:
sudo -i -u postgres
Sau khi chuyển đổi thành công, bạn có thể truy cập psql shell bằng cách gõ:
psql
Bước bảo mật đầu tiên và quan trọng nhất là đặt mật khẩu cho người dùng postgres. Mặc định, người dùng này không có mật khẩu. Chạy lệnh sau bên trong psql shell:
\password postgres
Hệ thống sẽ yêu cầu bạn nhập và xác nhận mật khẩu mới. Hãy chọn một mật khẩu mạnh để bảo vệ tài khoản quản trị này. Sau khi hoàn tất, bạn có thể thoát khỏi psql bằng lệnh \q và trở lại người dùng thông thường bằng lệnh exit.
Tạo user mới và phân quyền truy cập cơ sở dữ liệu
Việc sử dụng tài khoản postgres cho ứng dụng là một rủi ro bảo mật lớn. Thay vào đó, bạn nên tạo một người dùng riêng biệt với các quyền hạn được giới hạn chỉ trong phạm vi cơ sở dữ liệu của ứng dụng. Điều này tuân thủ nguyên tắc đặc quyền tối thiểu (principle of least privilege).
Hãy truy cập lại psql với tư cách người dùng postgres. Bây giờ, hãy tạo một người dùng mới cho ứng dụng Node.js. Ví dụ, chúng ta sẽ tạo người dùng có tên app_user với một mật khẩu an toàn:
CREATE USER app_user WITH PASSWORD 'your_strong_password';
Tiếp theo, bạn cần tạo cơ sở dữ liệu cho ứng dụng và chỉ định app_user làm chủ sở hữu. Chúng ta sẽ tìm hiểu kỹ hơn về việc tạo cơ sở dữ liệu ở phần sau. Giả sử bạn đã có cơ sở dữ liệu tên app_db, hãy cấp tất cả quyền trên cơ sở dữ liệu này cho người dùng vừa tạo:
GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;
Lệnh này cho phép app_user thực hiện mọi thao tác như tạo bảng, chèn, cập nhật, và xóa dữ liệu bên trong app_db. Việc phân tách người dùng và quyền hạn như thế này giúp tăng cường bảo mật và dễ dàng quản lý hơn trong các dự án thực tế.

Tạo và quản lý cơ sở dữ liệu PostgreSQL
Sau khi đã có người dùng dành riêng cho ứng dụng, bước tiếp theo là tạo một không gian làm việc riêng cho nó – một cơ sở dữ liệu. Đây là nơi tất cả các bảng, dữ liệu và logic liên quan đến ứng dụng của bạn sẽ được lưu trữ.
Tạo cơ sở dữ liệu mới
Việc tạo một cơ sở dữ liệu mới trong PostgreSQL là một thao tác đơn giản nhưng cần chú ý đến một vài tùy chọn quan trọng như encoding và collation để đảm bảo dữ liệu được lưu trữ và xử lý chính xác. Encoding UTF8 là lựa chọn phổ biến nhất hiện nay vì nó hỗ trợ hầu hết các ngôn ngữ trên thế giới, bao gồm cả tiếng Việt.
Vẫn trong psql shell với quyền của người dùng postgres, bạn có thể tạo một cơ sở dữ liệu mới tên là app_db và gán quyền sở hữu cho app_user đã tạo trước đó bằng lệnh sau:
CREATE DATABASE app_db OWNER app_user;
Để tùy chỉnh chi tiết hơn, bạn có thể chỉ định cả encoding và collation. Collation quy định cách sắp xếp và so sánh chuỗi ký tự.
CREATE DATABASE app_db
WITH
OWNER = app_user
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.UTF-8'
LC_CTYPE = 'en_US.UTF-8'
TABLESPACE = pg_default
CONNECTION LIMIT = -1;
Sau khi tạo xong, bạn có thể dùng lệnh \l trong psql để liệt kê tất cả các cơ sở dữ liệu hiện có và xác nhận rằng app_db đã được tạo thành công với chủ sở hữu là app_user.
Quản lý bảng và dữ liệu cơ bản
Khi đã có cơ sở dữ liệu, bạn cần tạo ra cấu trúc để lưu trữ dữ liệu, đó chính là các bảng (tables). Để thực hiện thao tác này, bạn cần kết nối vào cơ sở dữ liệu vừa tạo.
Trong psql, sử dụng lệnh \c để kết nối đến app_db:
\c app_db
Bây giờ, bạn có thể bắt đầu tạo bảng. Ví dụ, hãy tạo một bảng đơn giản để lưu thông tin sản phẩm:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price NUMERIC(10, 2) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
Trong đó, SERIAL PRIMARY KEY tự động tạo một ID duy nhất cho mỗi sản phẩm. Sau khi có bảng, bạn có thể thêm dữ liệu vào bằng lệnh INSERT:
INSERT INTO products (name, price) VALUES ('Laptop ABC', 25000000);
INSERT INTO products (name, price) VALUES ('Chuột không dây XYZ', 500000);
Và để truy vấn, xem lại dữ liệu đã thêm, bạn dùng lệnh SELECT:
SELECT * FROM products;
Đây là những thao tác cơ bản nhất giúp bạn tương tác với cơ sở dữ liệu của mình trực tiếp từ psql shell.
Kết nối Node.js với PostgreSQL bằng thư viện pg
Sau khi đã chuẩn bị xong phần cơ sở dữ liệu, đã đến lúc kết nối ứng dụng Node.js của bạn với PostgreSQL. Để làm được điều này, chúng ta sẽ sử dụng node-postgres (thường được gọi là pg), một thư viện phổ biến và mạnh mẽ để tương tác với PostgreSQL trong môi trường Node.js.
Cài đặt và cấu hình thư viện pg trong dự án Node.js
Đầu tiên, bạn cần khởi tạo một dự án Node.js (nếu chưa có) và cài đặt thư viện pg. Hãy di chuyển đến thư mục dự án của bạn trong terminal và chạy các lệnh sau:
npm init -y
npm install pg
Lệnh npm init -y sẽ tạo một tệp package.json mặc định, và npm install pg sẽ tải và cài đặt thư viện pg vào thư mục node_modules của dự án.
Tiếp theo, bạn cần cấu hình thông tin kết nối đến cơ sở dữ liệu PostgreSQL. Một phương pháp tốt nhất là sử dụng biến môi trường để lưu trữ các thông tin nhạy cảm này thay vì viết trực tiếp vào mã nguồn. Bạn có thể tạo một tệp .env và sử dụng thư viện dotenv để tải các biến này.
# .env file
DB_USER=app_user
DB_PASSWORD=your_strong_password
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=app_db
Bằng cách này, bạn có thể dễ dàng thay đổi thông tin kết nối giữa các môi trường phát triển, kiểm thử và sản phẩm mà không cần sửa đổi code.
Viết mã kết nối và truy vấn cơ bản trong Node.js
Thư viện pg cung cấp hai cách chính để kết nối: một client đơn lẻ (Client) hoặc một nhóm kết nối (Pool). Sử dụng Pool được khuyến khích trong hầu hết các ứng dụng web vì nó quản lý nhiều kết nối đồng thời, giúp tối ưu hóa hiệu năng và tránh tình trạng mở/đóng kết nối liên tục.
Đây là một đoạn mã mẫu để tạo một Pool và thực hiện một truy vấn SELECT đơn giản:
const { Pool } = require('pg');
// Tạo một pool kết nối mới
const pool = new Pool({
user: 'app_user',
host: 'localhost',
database: 'app_db',
password: 'your_strong_password',
port: 5432,
});
async function getProducts() {
try {
const client = await pool.connect();
console.log('Kết nối thành công đến PostgreSQL!');
const result = await client.query('SELECT * FROM products');
console.log('Dữ liệu sản phẩm:', result.rows);
client.release(); // Trả kết nối về lại pool
} catch (error) {
console.error('Lỗi khi kết nối hoặc truy vấn:', error.stack);
} finally {
await pool.end(); // Đóng tất cả kết nối trong pool
}
}
getProducts();

Khi chạy đoạn mã này, nó sẽ kết nối đến PostgreSQL, lấy tất cả dữ liệu từ bảng products và hiển thị ra console. Phương thức pool.connect() sẽ lấy một client từ pool, và client.release() sẽ trả nó về lại pool để có thể tái sử dụng cho các yêu cầu khác.
Thực hiện các thao tác cơ bản với cơ sở dữ liệu từ ứng dụng Node.js
Sau khi đã thiết lập kết nối thành công, bạn có thể bắt đầu thực hiện các thao tác CRUD (Create, Read, Update, Delete) từ ứng dụng Node.js của mình. Điều quan trọng là phải sử dụng các truy vấn tham số hóa (parameterized queries) để ngăn chặn các cuộc tấn công SQL Injection.
Thêm dữ liệu và kiểm tra phản hồi
Để thêm một bản ghi mới vào cơ sở dữ liệu, bạn sẽ sử dụng câu lệnh INSERT. Thư viện pg cho phép bạn truyền các giá trị một cách an toàn dưới dạng một mảng, giúp bảo vệ ứng dụng của bạn.
Hãy xem ví dụ về cách thêm một sản phẩm mới vào bảng products:
async function addProduct(name, price) {
const queryText = 'INSERT INTO products(name, price) VALUES($1, $2) RETURNING *';
const values = [name, price];
try {
const result = await pool.query(queryText, values);
console.log('Đã thêm sản phẩm thành công:', result.rows[0]);
return result.rows[0];
} catch (error) {
console.error('Lỗi khi thêm sản phẩm:', error.stack);
}
}
// Ví dụ sử dụng
addProduct('Bàn phím cơ K1', 1200000);

Trong ví dụ trên, $1 và $2 là các placeholder cho các giá trị sẽ được truyền vào. Thư viện pg sẽ tự động xử lý và làm sạch các giá trị này trước khi gửi đến cơ sở dữ liệu. Mệnh đề RETURNING * rất hữu ích, nó yêu cầu PostgreSQL trả về toàn bộ bản ghi vừa được tạo, giúp bạn có thể xác nhận hoặc sử dụng ngay lập tức.
Cập nhật, xóa dữ liệu và xử lý lỗi
Tương tự như thêm dữ liệu, việc cập nhật và xóa cũng nên được thực hiện bằng các truy vấn tham số hóa. Bạn sẽ sử dụng câu lệnh UPDATE để sửa đổi các bản ghi hiện có và DELETE để xóa chúng.
Đây là ví dụ về cách cập nhật giá của một sản phẩm dựa trên ID của nó:
async function updateProductPrice(productId, newPrice) {
const queryText = 'UPDATE products SET price = $1 WHERE id = $2 RETURNING *';
const values = [newPrice, productId];
try {
const result = await pool.query(queryText, values);
if (result.rowCount > 0) {
console.log('Cập nhật giá thành công:', result.rows[0]);
} else {
console.log(`Không tìm thấy sản phẩm có ID = ${productId}`);
}
} catch (error) {
console.error('Lỗi khi cập nhật sản phẩm:', error.stack);
}
}
// Ví dụ sử dụng
updateProductPrice(1, 26500000);
Và đây là cách xóa một sản phẩm:
async function deleteProduct(productId) {
const queryText = 'DELETE FROM products WHERE id = $1';
const values = [productId];
try {
const result = await pool.query(queryText, values);
if (result.rowCount > 0) {
console.log(`Đã xóa thành công sản phẩm có ID = ${productId}`);
} else {
console.log(`Không tìm thấy sản phẩm có ID = ${productId}`);
}
} catch (error) {
console.error('Lỗi khi xóa sản phẩm:', error.stack);
}
}
Việc sử dụng khối try...catch trong mọi thao tác với cơ sở dữ liệu là cực kỳ quan trọng. Nó giúp bạn bắt và xử lý các lỗi một cách linh hoạt, đảm bảo ứng dụng không bị dừng đột ngột và có thể đưa ra phản hồi phù hợp cho người dùng.
Các vấn đề thường gặp và cách xử lý
Trong quá trình phát triển, việc gặp phải lỗi là điều không thể tránh khỏi. Hiểu rõ nguyên nhân của các vấn đề phổ biến và cách khắc phục chúng sẽ giúp bạn tiết kiệm rất nhiều thời gian và công sức.
Lỗi kết nối PostgreSQL từ Node.js
Đây là một trong những lỗi phổ biến nhất khi bạn mới bắt đầu. Thông báo lỗi thường là “connection refused” hoặc “password authentication failed”.
Nguyên nhân phổ biến bao gồm:
- Sai thông tin kết nối: Kiểm tra lại
user,password,host,port, vàdatabasetrong cấu hình kết nối của Node.js. Một lỗi chính tả nhỏ cũng có thể gây ra vấn đề. - Dịch vụ PostgreSQL không chạy: Đảm bảo rằng dịch vụ PostgreSQL đang hoạt động trên máy chủ Ubuntu bằng lệnh
sudo systemctl status postgresql. Nếu không, hãy khởi động nó bằngsudo systemctl start postgresql. - Tường lửa (Firewall) chặn kết nối: Nếu ứng dụng Node.js và PostgreSQL không nằm trên cùng một máy, tường lửa có thể đang chặn cổng mặc định
5432. Trên Ubuntu, bạn có thể kiểm tra và cho phép kết nối bằng UFW:sudo ufw allow 5432/tcp. - Cấu hình lắng nghe của PostgreSQL: Mặc định, PostgreSQL chỉ lắng nghe các kết nối từ
localhost. Nếu bạn cần kết nối từ một máy khác, bạn phải chỉnh sửa tệppostgresql.conf(thay đổilisten_addresses) vàpg_hba.conf(để cho phép kết nối từ địa chỉ IP của ứng dụng).
Vấn đề quyền hạn user trong PostgreSQL
Một lỗi thường gặp khác là “permission denied for table [tên bảng]”. Lỗi này xảy ra khi người dùng mà ứng dụng Node.js đang sử dụng không có đủ quyền để thực hiện một hành động cụ thể (ví dụ: SELECT, INSERT, UPDATE).
Cách khắc phục:
- Kiểm tra quyền hiện tại: Đăng nhập vào
psqlvà sử dụng lệnh\dp [tên bảng]để xem các quyền hiện có trên bảng đó. - Cấp quyền cần thiết: Sử dụng lệnh
GRANTđể cấp các quyền còn thiếu. Ví dụ, để cấp quyềnSELECTvàINSERTtrên bảngproductscho người dùngapp_user, bạn sẽ chạy:
GRANT SELECT, INSERT ON TABLE products TO app_user;
- Quyền trên Sequence (cho cột SERIAL): Nếu bạn sử dụng cột
SERIALhoặcBIGSERIAL, người dùng cũng cần quyềnUSAGEtrên sequence liên quan. Lệnh sau sẽ cấp quyền này:
GRANT USAGE, SELECT ON SEQUENCE products_id_seq TO app_user;
Việc hiểu và quản lý tốt quyền hạn không chỉ giúp khắc phục lỗi mà còn là một phần quan trọng của việc bảo mật cơ sở dữ liệu.
Một số lưu ý và best practices khi triển khai trên môi trường Ubuntu 20.04
Để xây dựng một hệ thống bền vững, hiệu quả và an toàn, việc tuân thủ các nguyên tắc và thực tiễn tốt nhất là điều cần thiết. Dưới đây là một số lưu ý quan trọng khi bạn làm việc với Node.js và PostgreSQL trên Ubuntu.
- Tăng cường bảo mật người dùng và cơ sở dữ liệu: Luôn tạo một người dùng riêng cho mỗi ứng dụng với các quyền hạn được giới hạn nghiêm ngặt. Tuyệt đối không sử dụng người dùng
postgrescho ứng dụng. Sử dụng mật khẩu mạnh và định kỳ thay đổi chúng. - Sử dụng connection pool để tối ưu hiệu năng: Thay vì tạo kết nối mới cho mỗi yêu cầu, hãy sử dụng connection pool. Thư viện
pgđã tích hợp sẵn tính năng này thông qua lớpPool. Nó giúp tái sử dụng các kết nối hiện có, giảm độ trễ và áp lực lên cơ sở dữ liệu, từ đó cải thiện đáng kể hiệu năng của ứng dụng. - Cấu hình tự động sao lưu dữ liệu định kỳ: Dữ liệu là tài sản quý giá nhất. Hãy thiết lập một cơ chế sao lưu tự động bằng cách sử dụng tiện ích
pg_dumpvà một công cụ lập lịch nhưcron. Một kịch bản sao lưu đơn giản có thể được chạy hàng đêm để tạo một bản sao của cơ sở dữ liệu và lưu trữ nó ở một nơi an toàn. - Tránh hardcode thông tin kết nối trong mã nguồn: Không bao giờ viết trực tiếp thông tin nhạy cảm như tên người dùng, mật khẩu, hay địa chỉ máy chủ vào code. Thay vào đó, hãy sử dụng các biến môi trường. Các thư viện như
dotenvgiúp bạn dễ dàng quản lý các biến này trong môi trường phát triển, trong khi ở môi trường sản phẩm, bạn có thể thiết lập chúng trực tiếp trên hệ điều hành hoặc thông qua các công cụ quản lý cấu hình.

Việc áp dụng những thực tiễn này không chỉ giúp ứng dụng của bạn chạy ổn định hơn mà còn dễ dàng bảo trì và mở rộng trong tương lai.
Kết luận
Qua bài viết này, chúng ta đã cùng nhau đi qua một hành trình chi tiết để xây dựng một ứng dụng Node.js kết nối với cơ sở dữ liệu PostgreSQL trên nền tảng Ubuntu 20.04. Từ những bước đầu tiên như cài đặt và cập nhật hệ thống, cho đến các thao tác phức tạp hơn như cấu hình người dùng, tạo cơ sở dữ liệu, và viết mã tương tác, bạn đã có được một nền tảng vững chắc. Các bước quan trọng đã được tóm lược bao gồm cài đặt PostgreSQL, bảo mật người dùng quản trị, tạo người dùng và cơ sở dữ liệu riêng cho ứng dụng, kết nối bằng thư viện pg và thực hiện các thao tác CRUD một cách an toàn.
Đây mới chỉ là bước khởi đầu. Để trở thành một nhà phát triển back-end chuyên nghiệp, chúng tôi khuyến khích bạn tiếp tục tìm hiểu các kỹ thuật nâng cao hơn. Hãy khám phá về ORM (Object-Relational Mapping) như Sequelize hoặc Prisma để làm việc với cơ sở dữ liệu một cách trực quan hơn, nghiên cứu về cách tối ưu hóa truy vấn (query optimization) và sử dụng chỉ mục (indexing) để tăng tốc độ truy xuất dữ liệu.
Con đường học hỏi là vô tận, nhưng với nền tảng kiến thức bạn đã có được hôm nay, bạn hoàn toàn đủ khả năng để bắt đầu triển khai các dự án thực tế. Hãy bắt tay vào thực hành, thử nghiệm và xây dựng những ứng dụng tuyệt vời của riêng mình. Chúc bạn thành công trên hành trình chinh phục công nghệ