Hướng dẫn sử dụng PostgreSQL với Node.js trên Ubuntu 20.04: Cài đặt, cấu hình và thao tác cơ bản

Chào bạn, tôi là Đức, người đứng sau blog buimanhduc.com. Với kinh nghiệm nhiều năm trong lĩnh vực phát triển web và làm việc với các hệ thống backend, tôi hiểu rằng việc kết hợp một cơ sở dữ liệu mạnh mẽ như PostgreSQL là gì với một môi trường linh hoạt như Node.js là gì trên server Ubuntu là một kỹ năng cực kỳ giá trị. Tuy nhiên, quá trình cài đặt và cấu hình ban đầu có thể gây không ít khó khăn cho những người mới bắt đầu. Chính vì vậy, bài viết này ra đời với mục tiêu trở thành một cuốn cẩm nang chi tiết, dẫn dắt bạn đi qua từng bước, từ con số không đến khi có thể tự tin xây dựng một ứng dụng Node.js hoàn chỉnh kết nối với PostgreSQL trên Ubuntu 20.04. Chúng ta sẽ cùng nhau khám phá từ việc cài đặt, bảo mật, tạo database, cho đến việc viết code để thao tác dữ liệu và xử lý các lỗi thường gặp.

Giới thiệu

PostgreSQL, thường được gọi là Postgres, là một hệ quản trị cơ sở dữ liệu quan hệ đối tượng (ORDBMS) mã nguồn mở nổi tiếng với sự ổn định, mạnh mẽ và tuân thủ chuẩn SQL là gì. Trong khi đó, Node.js là một môi trường chạy JavaScript là gì phía máy chủ, cho phép xây dựng các ứng dụng back-end hiệu năng cao và có khả năng mở rộng tốt. Việc kết hợp hai công nghệ này trên một hệ điều hành máy chủ phổ biến như Ubuntu 20.04 tạo nên một bộ ba hoàn hảo để phát triển các ứng dụng web hiện đại.

Tuy nhiên, việc triển khai hệ thống này không phải lúc nào cũng suôn sẻ. Nhiều nhà phát triển, đặc biệt là những người mới, thường gặp phải các vấn đề như cài đặt sai cách, cấu hình bảo mật thiếu sót, hay lỗi kết nối giữa ứng dụng và cơ sở dữ liệu. Bài viết này sẽ cung cấp một giải pháp toàn diện và chi tiết. Chúng ta sẽ đi qua từng bước, từ cài đặt PostgreSQL trên Ubuntu, cấu hình người dùng, tạo cơ sở dữ liệu, đến việc sử dụng thư viện pg trong Node.js để kết nối và thực hiện các thao tác dữ liệu. Cuối cùng, bài viết cũng sẽ đề cập đến các lỗi thường gặp và những phương pháp tối ưu tốt nhất.

Cài đặt PostgreSQL trên Ubuntu 20.04

Để bắt đầu, chúng ta cần chuẩn bị môi trường Ubuntu 20.04 và tiến hành cài đặt PostgreSQL. Đây là bước nền tảng quan trọng nhất để đảm bảo hệ thống hoạt động ổn định.

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, việc đầu tiên và quan trọng nhất là đảm bảo hệ thống của bạn được cập nhật lên phiên bản mới nhất. Điều này giúp vá các lỗ hổng bảo mật và đảm bảo tính tương thích của các gói phần mềm. Bạn có thể kiểm tra phiên bản Ubuntu của mình bằng lệnh lsb_release -a.

Hãy mở terminal và chạy hai lệnh sau để cập nhật danh sách gói và nâng cấp các gói đã cài đặt:

sudo apt update
sudo apt upgrade

Quá trình này có thể mất vài phút tùy thuộc vào tốc độ mạng và số lượng gói cần cập nhật. Hãy kiên nhẫn chờ đợi cho đến khi hoàn tất.

Hình minh họa

Hướng dẫn cài đặt PostgreSQL từ repository chính thức

Ubuntu 20.04 đã có sẵn PostgreSQL trong kho lưu trữ (repository) 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 PostgreSQL server và gói postgresql-contrib, một gói chứa các tiện ích và chức năng mở rộng hữu ích.

Trong terminal, hãy thực thi lệnh sau:

sudo apt install postgresql postgresql-contrib

Sau khi quá trình cài đặt hoàn tất, dịch vụ PostgreSQL sẽ tự động được khởi chạy. Để chắc chắn mọi thứ hoạt động đúng cách, bạn có thể kiểm tra trạng thái của dịch vụ bằng lệnh:

systemctl status postgresql.service

Nếu bạn thấy dòng chữ active (running) màu xanh lá, xin chúc mừng, bạn đã cài đặt thành công PostgreSQL trên server của mình.

Hình minh họa

Cấu hình và tạo người dùng cơ sở dữ liệu PostgreSQL

Sau khi cài đặt xong, bước tiếp theo là cấu hình bảo mật ban đầu và tạo một người dùng riêng cho ứng dụng Node.js. Việc này giúp tăng cường bảo mật và quản lý quyền truy cập một cách hiệu quả, thay vì sử dụng tài khoản quản trị cao nhất.

Truy cập PostgreSQL shell và bảo mật ban đầu

Trong quá trình cài đặt, PostgreSQL tạo ra một người dùng mặc định tên là postgres. Đây là tài khoản có quyền cao nhất, tương tự như tài khoản root trên hệ thống. Để truy cập vào giao diện dòng lệnh của PostgreSQL (psql), bạn cần chuyển sang người dùng postgres của hệ thống và sau đó khởi chạy psql.

Sử dụng lệnh sau để truy cập:

sudo -i -u postgres psql

Lúc này, bạn đã ở bên trong psql shell. Việc đầu tiên cần làm để tăng cường bảo mật là đặt mật khẩu cho người dùng postgres. Hãy chạy lệnh sau và nhập mật khẩu mới của bạn:

\password postgres

Sau khi hoàn tất, bạn có thể thoát khỏi psql bằng lệnh \q.

Hình minh họa

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 đó, chúng ta nên tạo một người dùng mới với quyền hạn giới hạn, chỉ đủ để thực hiện các công việc cần thiết cho ứng dụng Node.js.

Hãy truy cập lại psql shell như bước trên. Sau đó, sử dụng lệnh CREATE USER để tạo một người dùng mới. Ví dụ, để tạo người dùng có tên là app_user với mật khẩu là your_strong_password, bạn chạy lệnh:

CREATE USER app_user WITH PASSWORD 'your_strong_password';

Tiếp theo, bạn cần cấp cho người dùng này các quyền hạn cần thiết trên một cơ sở dữ liệu cụ thể (chúng ta sẽ tạo ở phần sau). Ví dụ, để cấp toàn bộ quyền trên database app_db cho app_user, bạn sẽ dùng lệnh:

GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;

Việc phân quyền chi tiết như vậy giúp đảm bảo rằng nếu tài khoản ứng dụng bị xâm phạm, kẻ tấn công cũng không thể truy cập hay phá hoại các cơ sở dữ liệu khác trên cùng server.

Tạo và quản lý cơ sở dữ liệu PostgreSQL

Khi đã có người dùng riêng cho ứng dụng, bước tiếp theo là tạo ra một cơ sở dữ liệu để lưu trữ dữ liệu. Chúng ta cũng sẽ tìm hiểu sơ qua cách tạo bảng và thực hiện các truy vấn cơ bản.

Tạo cơ sở dữ liệu mới

Việc tạo một database mới trong PostgreSQL rất đơn giản. Bạn có thể thực hiện việc này ngay trong psql shell bằng lệnh CREATE DATABASE. Một thực hành tốt là chỉ định luôn người dùng sở hữu (owner) của database đó. Điều này giúp việc quản lý quyền trở nên rõ ràng hơn.

Ví dụ, để tạo một database có tên là app_db và gán quyền sở hữu cho app_user mà chúng ta đã tạo ở phần trước, hãy dùng lệnh sau:

CREATE DATABASE app_db OWNER app_user;

Ngoài ra, khi tạo database, bạn cũng có thể chỉ định các tùy chọn quan trọng như encoding (bảng mã) và collation (quy tắc so sánh và sắp xếp ký tự). Để hỗ trợ tiếng Việt có dấu, bạn nên sử dụng UTF8. Lệnh đầy đủ có thể trông như sau:

CREATE DATABASE app_db OWNER app_user ENCODING 'UTF8' LC_COLLATE='en_US.UTF-8' LC_CTYPE='en_US.UTF-8' TEMPLATE template0;

Tuy nhiên, trong hầu hết các trường hợp, lệnh đơn giản ở trên là đủ.

Hình minh họa

Quản lý bảng và dữ liệu cơ bản

Sau khi có database, bạn cần kết nối vào đó để bắt đầu tạo bảng và thao tác dữ liệu. Trong psql, bạn có thể chuyển database bằng lệnh \c tên_database. Ví dụ:

\c app_db

Bây giờ, bạn có thể tạo một bảng đơn giản để lưu trữ thông tin người dùng. Ví dụ, một bảng users với các cột id, username, và email:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

SERIAL PRIMARY KEY sẽ tự động tạo một số nguyên tự tăng và làm khóa chính cho mỗi bản ghi. Sau khi tạo bảng, bạn có thể thử thêm một vài dữ liệu mẫu bằng lệnh INSERT và truy vấn chúng bằng lệnh SELECT:

INSERT INTO users (username, email) VALUES ('manhduc', 'contact@buimanhduc.com');
SELECT * FROM users;

Thao tác thành công các lệnh này cho thấy database và bảng của bạn đã sẵn sàng để ứng dụng Node.js kết nối vào.

Kết nối Node.js với PostgreSQL bằng thư viện pg

Đây là phần cốt lõi của bài viết, nơi chúng ta sẽ kết nối ứng dụng Node.js với cơ sở dữ liệu PostgreSQL vừa tạo. Chúng ta sẽ sử dụng pg, thư viện phổ biến và được tin dùng nhất cho công việc này.

Cài đặt và cấu hình thư viện pg trong dự án Node.js

Đầu tiên, bạn cần có một dự án Node.js. Nếu chưa có, hãy tạo một thư mục mới, di chuyển vào đó và chạy npm init -y để tạo file package.json. Bạn có thể tìm hiểu thêm về npm là gì để quản lý các package trong dự án.

Tiếp theo, cài đặt thư viện pg bằng npm:

npm install pg

Để kết nối tới PostgreSQL, bạn cần cung cấp thông tin kết nối, bao gồm host, port, tên database, user và mật khẩu. Cách tốt nhất là lưu trữ các thông tin này trong các biến môi trường thay vì viết thẳng vào code. Tuy nhiên, để đơn giản hóa ví dụ, chúng ta sẽ tạo một đối tượng cấu hình.

Tạo một file, ví dụ db.js, và định nghĩa thông tin kết nối:

const { Client } = require('pg');

const client = new Client({
  user: 'app_user',
  host: 'localhost',
  database: 'app_db',
  password: 'your_strong_password',
  port: 5432,
});

Hãy thay thế các giá trị trên bằng thông tin chính xác của bạn.

Hình minh họa

Viết mã kết nối và truy vấn cơ bản trong Node.js

Với đối tượng client đã được cấu hình, việc kết nối và truy vấn trở nên rất trực quan. Bạn cần gọi phương thức connect() để mở kết nối, query() để thực thi câu lệnh SQL, và cuối cùng là end() để đóng kết nối khi không cần dùng nữa.

Dưới đây là một đoạn mã hoàn chỉnh để kết nối và thực hiện một truy vấn đơn giản là lấy thời gian hiện tại từ server PostgreSQL:

const { Client } = require('pg');

const client = new Client({
  user: 'app_user',
  host: 'localhost',
  database: 'app_db',
  password: 'your_strong_password',
  port: 5432,
});

async function testConnection() {
  try {
    await client.connect();
    console.log('Kết nối tới PostgreSQL thành công!');
    const res = await client.query('SELECT NOW()');
    console.log('Thời gian hiện tại từ server:', res.rows[0].now);
    await client.end();
  } catch (err) {
    console.error('Lỗi kết nối hoặc truy vấn:', err.stack);
  }
}

testConnection();

Hãy chạy file này bằng lệnh node db.js. Nếu bạn thấy thông báo kết nối thành công và thời gian hiện tại, nghĩa là mọi thứ đã được thiết lập chính xác.

Hình minh họa

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

Khi đã kết nối thành công, chúng ta 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. Một lưu ý quan trọng là luôn sử dụng truy vấn tham số hóa (parameterized queries) để tránh lỗi SQL Injection. Bạn cũng có thể tìm hiểu thêm về ORM là gì để làm việc hiệu quả hơn với cơ sở dữ liệu như PostgreSQL.

Thêm dữ liệu và kiểm tra phản hồi

Để thêm một bản ghi mới vào bảng users, chúng ta sẽ sử dụng câu lệnh INSERT. Thư viện pg cho phép bạn truyền các giá trị dưới dạng một mảng, giúp mã nguồn an toàn và dễ đọc hơn. Các tham số trong câu lệnh SQL được đánh dấu bằng $1, $2,…

Hãy xem ví dụ sau về cách tạo một hàm để thêm người dùng mới:

async function addUser(username, email) {
  try {
    await client.connect();
    const query = 'INSERT INTO users(username, email) VALUES($1, $2) RETURNING *';
    const values = [username, email];
    const res = await client.query(query, values);
    console.log('Đã thêm người dùng thành công:', res.rows[0]);
    await client.end();
    return res.rows[0];
  } catch (err) {
    console.error('Lỗi khi thêm người dùng:', err);
  }
}

// Gọi hàm để thêm người dùng mới
// addUser('newuser', 'new@example.com');

Mệnh đề RETURNING * sẽ trả về toàn bộ thông tin của bản ghi vừa được thêm, rất hữu ích để xác nhận kết quả.

Hình minh họa

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 (UPDATE) và xóa (DELETE) cũng nên được thực hiện bằng truy vấn tham số hóa. Điều quan trọng khi thực hiện các thao tác này là phải xử lý lỗi một cách cẩn thận, vì có thể xảy ra các tình huống như không tìm thấy bản ghi để cập nhật/xóa, hoặc dữ liệu mới vi phạm ràng buộc (ví dụ: email trùng lặp).

Dưới đây là ví dụ về hàm cập nhật email của người dùng và hàm xóa người dùng theo ID:

async function updateUserEmail(id, newEmail) {
  const query = 'UPDATE users SET email = $1 WHERE id = $2 RETURNING *';
  const values = [newEmail, id];
  // ... (logic kết nối và xử lý lỗi tương tự)
  const res = await client.query(query, values);
  if (res.rowCount > 0) {
    console.log('Cập nhật thành công:', res.rows[0]);
  } else {
    console.log('Không tìm thấy người dùng để cập nhật.');
  }
}

async function deleteUser(id) {
  const query = 'DELETE FROM users WHERE id = $1';
  const values = [id];
  // ... (logic kết nối và xử lý lỗi)
  const res = await client.query(query, values);
  if (res.rowCount > 0) {
    console.log(`Đã xóa thành công người dùng có ID: ${id}`);
  } else {
    console.log('Không tìm thấy người dùng để xóa.');
  }
}

Sử dụng khối try...catch để bắt các lỗi tiềm ẩn từ cơ sở dữ liệu là một thực hành bắt buộc để xây dựng ứng dụng ổn định và đáng tin cậy.

Các vấn đề thường gặp và cách xử lý

Trong quá trình làm việc, bạn có thể sẽ gặp phải một số lỗi phổ biến. Hiểu rõ nguyên nhân và cách khắc phục 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à lỗi phổ biến nhất, thường có thông báo như connection refused hoặc password authentication failed. Có một vài nguyên nhân chính bạn nên kiểm tra:

  1. Sai thông tin kết nối: Kiểm tra lại host, port, database, user, và đặc biệt là password trong đối tượng cấu hình của bạn. Một lỗi chính tả nhỏ cũng có thể gây ra vấn đề.
  2. Dịch vụ PostgreSQL chưa chạy: Đảm bảo rằng dịch vụ PostgreSQL đang hoạt động trên server bằng lệnh systemctl status postgresql.service.
  3. Firewall chặn kết nối: Nếu ứng dụng Node.jsPostgreSQL không chạy trên cùng một máy, hãy kiểm tra xem firewall (tường lửa) có đang chặn port 5432 hay không. Bạn có thể cần phải thêm một quy tắc để cho phép kết nối.
  4. Cấu hình pg_hba.conf: Đây là file cấu hình xác thực của PostgreSQL. Mặc định, nó có thể không cho phép kết nối từ bên ngoài localhost. Bạn cần chỉnh sửa file này để cho phép kết nối từ địa chỉ IP của ứng dụng.

Hình minh họa

Vấn đề quyền hạn user trong PostgreSQL

Một lỗi phổ biến 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 (app_user trong ví dụ của chúng ta) không có đủ quyền để thực hiện một hành động nào đó (ví dụ SELECT, INSERT).

Để khắc phục, bạn cần truy cập vào psql và cấp các quyền cần thiết cho người dùng. Ví dụ, để cấp quyền SELECT, INSERT, UPDATE, DELETE trên bảng users cho app_user:

GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE users TO app_user;

Nếu bạn muốn cấp các quyền này cho tất cả các bảng trong một schema (ví dụ public), bạn có thể dùng lệnh:

GRANT ALL ON ALL TABLES IN SCHEMA public TO app_user;

Sau khi cấp quyền, hãy thử chạy lại ứng dụng của bạn. Vấn đề sẽ được giải quyết.

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 ứng dụng chuyên nghiệp và hiệu quả, việc tuân theo các best practices là vô cùng quan trọng. Dưới đây là một số lưu ý bạn nên áp dụng.

  • Tăng cường bảo mật: Luôn tạo một người dùng riêng với quyền hạn tối thiểu cho ứng dụng. Tuyệt đối không sử dụng tài khoản postgres trong code. Sử dụng mật khẩu mạnh và thay đổi định kỳ.
  • Sử dụng connection pool: Thay vì tạo kết nối mới cho mỗi truy vấn (new Client()), hãy sử dụng connection pool (ví dụ: pg-pool). Pool sẽ quản lý và tái sử dụng một nhóm các kết nối, giúp giảm độ trễ và tăng hiệu năng đáng kể, đặc biệt khi ứng dụng có nhiều người dùng đồng thời.
  • Cấu hình tự động sao lưu dữ liệu: Dữ liệu là tài sản quý giá. Hãy thiết lập một cơ chế sao lưu tự động bằng cách sử dụng pg_dumpcron job trên Ubuntu. Điều này đảm bảo bạn có thể khôi phục dữ liệu trong trường hợp xảy ra sự cố.
  • Tránh hardcode thông tin kết nối: Không bao giờ viết thẳng thông tin nhạy cảm như mật khẩu database vào mã nguồn. Thay vào đó, hãy sử dụng các biến môi trường (environment variables) để lưu trữ chúng. Các thư viện như dotenv có thể giúp bạn quản lý việc này một cách dễ dàng trong môi trường phát triển.

Hình minh họa

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 để kết hợp sức mạnh của PostgreSQLNode.js 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, cấu hình bảo mật cho PostgreSQL, tạo người dùng và cơ sở dữ liệu, cho đến việc viết mã Node.js để kết nối và thực hiện các thao tác dữ liệu một cách an toàn. Chúng ta cũng đã tìm hiểu cách xử lý các lỗi thường gặp và các phương pháp hay nhất để triển khai một ứng dụng ổn định và hiệu quả.

Việc nắm vững các kỹ năng này sẽ mở ra cho bạn rất nhiều cơ hội để xây dựng các ứng dụng back-end mạnh mẽ và đáng tin cậy. Tuy nhiên, đây chỉ là bước khởi đầu. Tôi khuyến khích bạn tiếp tục tìm hiểu sâu hơn về các kỹ thuật nâng cao như sử dụng ORM (Object-Relational Mapping) như Sequelize hoặc TypeORM để làm việc với cơ sở dữ liệu một cách trực quan hơn, tối ưu hóa các câu truy vấn phức tạp, và tìm hiểu về indexing để tăng tốc độ truy xuất dữ liệu.

Chúc bạn thành công trên con đường trở thành một nhà phát triển chuyên nghiệp. Đừng ngần ngại thực hành và áp dụng những kiến thức này vào các dự án thực tế của riêng mình

Đánh giá
Tác giả

Mạnh Đức

Có cao nhân từng nói rằng: "Kiến thức trên thế giới này đầy rẫy trên internet. Tôi chỉ là người lao công cần mẫn đem nó tới cho người cần mà thôi !"

Chia sẻ
Bài viết liên quan