Kết nối nodejs với postgresql

  • Giới thiệu
  • PostgreSQL
  • Cấu hình dự án
  • Thực hiện các hoạt động CRUD
    • Cấu hình cơ sở dữ liệu
    • Kết nối với Cơ sở dữ liệu
    • Tạo bảng
    • Tạo / Chèn dữ liệu
    • Lấy / Chọn dữ liệu
    • Cập nhật dữ liệu
    • Xóa dữ liệu
  • Gộp chung
    • Tạo một nhóm
  • Sử dụng con trỏ để đọc các truy vấn lớn
  • Sự kết luận

Giới thiệu

Trong bài viết này, chúng tôi sẽ thảo luận về cách tích hợp PostgreSQL với Node.js.

Để làm theo bài viết này tốt hơn, chúng tôi khuyên bạn nên có kinh nghiệm sử dụng Node.js và câu lệnh SQL trước đó. Chúng tôi sẽ sử dụng cú pháp javascript ES6 đơn giản trong bài viết này.

Có một số ứng dụng khách khác nhau mà bạn có thể sử dụng để tích hợp PostgreSQL với Node.js. Trong bài viết này, chúng tôi sẽ sử dụng node-postgres. Nó là một mô-đun phổ biến và trưởng thành so với các ứng dụng khách PostgreSQL khác.

Ngoài ra, bạn có thể sử dụng PostgreSQL với ORM chẳng hạn như Trình tự cũng. Nhưng chúng tôi sẽ không sử dụng mô-đun ORM như vậy trong bài viết này. Thay vào đó, chúng tôi sẽ sử dụng các truy vấn SQL thuần túy, sau đó bạn có thể xây dựng các truy vấn này cho các tương tác cơ sở dữ liệu phức tạp hơn.

PostgreSQL

PostgreSQL là một cơ sở dữ liệu SQL phổ biến. Nó đã được phát triển tích cực trong hơn 30 năm qua và được coi là một trong những cơ sở dữ liệu quan hệ tiên tiến nhất hiện có. PostgreSQL cũng dễ học và dễ thiết lập so với các cơ sở dữ liệu quan hệ khác có sẵn. Do tính chất miễn phí và mã nguồn mở, đây là một lựa chọn phổ biến của các công ty khởi nghiệp.

PostgreSQL là một cơ sở dữ liệu đa nền tảng chạy trên tất cả các hệ điều hành chính. Tuy nhiên, cấu hình và quyền truy cập / tạo cơ sở dữ liệu hơi khác nhau giữa các hệ điều hành.

Trong bài viết này, chúng tôi sẽ sử dụng Ubuntu 18.04, một nền tảng Linux phổ biến và bao gồm PostgreSQL theo mặc định. Một số bước có thể hơi khác một chút nếu bạn đang sử dụng một hệ điều hành khác.

Cấu hình dự án

Hãy bắt đầu với một dự án Node.js trống đơn giản với cài đặt mặc định:

$ npm init -y

Sau đó, hãy sử dụng npm để cài đặt node-postgressẽ được sử dụng để kết nối và tương tác với Postgres:

$ npm install --save pg

Thực hiện các hoạt động CRUD

Với dự án của chúng ta đã khởi động, hãy tiếp tục và cấu hình cơ sở dữ liệu. Sau đó, chúng tôi sẽ viết một số chức năng CRUD cơ bản.

Cấu hình cơ sở dữ liệu

Như với tất cả các cơ sở dữ liệu quan hệ, chúng ta sẽ bắt đầu bằng cách tạo một cơ sở dữ liệu và kết nối với nó. Bạn có thể sử dụng CLI hoặc ứng dụng khách dựa trên GUI để thực hiện việc này. Vì nó rất đơn giản để thiết lập thông qua CLI, chúng tôi sẽ làm điều đó.

Đối với Ubuntu, mặc định psql sẽ nhập CLI. PostgreSQL sẽ tạo một người dùng được gọi là postgres để truy cập cơ sở dữ liệu trong các nền tảng dựa trên Linux. Do đó, chúng ta có thể sử dụng lệnh sau để đăng nhập với tư cách là postgres:

$ sudo -i -u postgres

Sau đó nhập CLI bằng cách chạy:

$ psql

Bạn sẽ thấy một trình bao lệnh tương tự như sau:

Để xem các cơ sở dữ liệu hiện tại, hãy sử dụng list hoặc l:

Và bây giờ, hãy tạo riêng của chúng ta bằng cách sử dụng truy vấn SQL:

CREATE DATABASE testdb;

Bằng cách chạy lệnh này, chúng tôi đang tạo testdb cơ sở dữ liệu và chào đón bằng đầu ra, xác nhận lệnh của chúng tôi:

CREATE DATABASE

Vì cơ sở dữ liệu đã được tạo nên bây giờ chúng ta có thể truy cập nó. Trong khi PostgreSQL tạo ra một postgres, mật khẩu không được đặt theo mặc định. Nếu bạn muốn đặt mật khẩu của mình (thay vì để trống), hãy sử dụng password:

Với mật khẩu của bạn đã đặt, chúng tôi đã sẵn sàng sử dụng cơ sở dữ liệu trong dự án của mình.

Kết nối với Cơ sở dữ liệu

Bạn có hai tùy chọn để có thể kết nối với máy chủ PostgreSQL bằng node-postgres. Một trong những tùy chọn là sử dụng một ứng dụng khách. Phương pháp khác là sử dụng một nhóm kết nối. Tuy nhiên, nếu ứng dụng của bạn sử dụng cơ sở dữ liệu thường xuyên, pool sẽ là một lựa chọn tốt hơn so với việc sử dụng một ứng dụng khách.

Kết nối với cơ sở dữ liệu bằng cách sử dụng node-postgres có thể được thực hiện theo hai cách – sử dụng khách hàng duy nhất và sử dụng một nhóm kết nối.

Chúng ta sẽ xem xét cách sử dụng nhóm kết nối để kết nối với cơ sở dữ liệu ở phần sau của bài viết này. Hiện tại, hãy kết nối với cơ sở dữ liệu bằng một ứng dụng khách để ngắn gọn và đơn giản:

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

const client = new Client({
    user: 'postgres',
    host: 'localhost',
    database: 'testdb',
    password: '1234abcd',
    port: 5432,
});

client.connect();

Ở đây, chúng tôi đã định cấu hình các tùy chọn theo cách thủ công. Tuy nhiên, bạn có thể kết nối với cơ sở dữ liệu mà không cần chuyển bất kỳ cơ sở dữ liệu nào:

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

const client = new Client();
client.connect();

Nhưng sau đó một lần nữa, Node nhu cầu để biết thế nào để kết nối với cơ sở dữ liệu, vì vậy chúng tôi sẽ cung cấp chúng thông qua các biến môi trường:

PGUSER=dbuser
PGHOST=database.server.com
PGPASSWORD=secretpassword
PGDATABASE=mydb
PGPORT=3211

Nếu bạn chưa tự định cấu hình chúng, mô-đun sẽ sử dụng các giá trị mặc định:

PGHOST='localhost'
PGUSER=process.env.USER
PGDATABASE=process.env.USER
PGPASSWORD=null
PGPORT=5432

Trên Linux, process.env.USER sẽ giữ giá trị cho người dùng hiện tại đã đăng nhập.

Tạo bảng

Với cơ sở dữ liệu được chuẩn bị sẵn để chèn dữ liệu, hãy tạo một số bảng để lưu trữ dữ liệu của chúng ta. Giống như với tất cả các cơ sở dữ liệu dựa trên SQL, chúng ta sẽ sử dụng CREATE TABLEry:

CREATE TABLE [table_name] (
    [column1] [datatype],
    [column2] [datatype],
    [column3] [datatype],
   ....
);

Một bảng bao gồm cộtvà mỗi cột có một kiểu dữ liệu. Ví dụ, một firstName sẽ có varchar là kiểu dữ liệu, đại diện cho một Chuỗi có kích thước thay đổi.

Nếu bạn muốn đọc thêm về các loại dữ liệu được hỗ trợ, Tài liệu PostgreSQL liệt kê chúng một cách độc đáo.

Điều đó đang được nói, chúng ta có thể sử dụng truy vấn này để tạo một bảng trong cơ sở dữ liệu:

const query = `
CREATE TABLE users (
    email varchar,
    firstName varchar,
    lastName varchar,
    age int
);
`;

Để thực sự chạy truy vấn này dựa trên cơ sở dữ liệu, chúng tôi sử dụng query() từ client chúng tôi đã thiết lập trước đây:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log('Table is successfully created');
    client.end();
});

Ghi chú: Đừng quên end() kết nối của bạn với máy khách sau khi bạn đã chạy truy vấn.

Chạy mã này sẽ tạo bảng của chúng tôi và in ra:

Table is successfully created

Điều này cũng có thể đạt được bằng cách sử dụng các lời hứa và async/await. Vì một cuộc gọi cơ sở dữ liệu có thể không thành công, nên sẽ hợp lý hơn khi sử dụng các lời hứa:

client
    .query(query)
    .then(res => {
        console.log('Table is successfully created');
    })
    .catch(err => {
        console.error(err);
    })
    .finally(() => {
        client.end();
    });

Như bạn có thể thấy trong ví dụ, chúng ta có thể sử dụng khối cuối cùng để đóng kết nối với cơ sở dữ liệu. Vì vậy, ngay cả khi truy vấn ném một errKết nối sẽ được đóng lại.

Ngoài ra, chúng ta có thể sử dụng async/await cú pháp nữa:

try {
    const res = await client.query(query);
    console.log('Table is successfully created');
} catch (err) {
    console.log(err.stack);
} finally {
    client.close();
}

Tất cả các cách tiếp cận này sẽ mang lại cùng một kết quả:

Table is successfully created

Để xác minh điều này, hãy sử dụng psql giao diện dòng để kiểm tra DB. Mở một thiết bị đầu cuối, bắt đầu trình bao bằng psqlvà chọn cơ sở dữ liệu bằng cách sử dụng c [database]. c là viết tắt của connect:

c testdb

Sau đó, bạn có thể liệt kê các bảng trong cơ sở dữ liệu testdb bằng cách chạy dt:

Bạn cũng có thể truy vấn các bảng cụ thể bằng cách cung cấp tên của chúng:

testdb=# dt FOO

Truy vấn này sẽ hiển thị bảng có tên FOO.

Tạo / Chèn dữ liệu

Chúng ta có thể sử dụng SQL INSERT INTO câu lệnh để chèn dữ liệu vào bảng:

INSERT INTO [table_name] ([column1], [column2], [column3], ...)
VALUES ([value1], [value2], [value3], ...);

Để làm cho truy vấn này trở nên cụ thể, hãy chèn các giá trị của riêng chúng ta và tạo một truy vấn:

const query = `
INSERT INTO users (email, firstName, lastName, age)
VALUES ('[email protected]', 'john', 'doe', 21)
`;

Và cuối cùng, hãy chạy truy vấn đối với cơ sở dữ liệu:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log('Data insert successful');
    client.end();
});

Ghi chú: Giống như lần trước, hàm này có thể được viết bằng cách sử dụng async/await cú pháp. Các ví dụ bổ sung này được bỏ qua cho ngắn gọn.

Chạy mã này sẽ chèn một người dùng vào cơ sở dữ liệu của chúng tôi và in ra:

Data insert successful

Để xác minh điều này, trong testdb cơ sở dữ liệu, chạy SELECT bản tường trình:

SELECT * from users;

Rõ ràng chúng ta có thể thấy rằng người dùng đã thực sự được tạo thành công:

Lấy / Chọn dữ liệu

Để truy xuất dữ liệu từ cơ sở dữ liệu, SELECT câu lệnh được sử dụng:

SELECT [column1], [column2], ...
FROM [table_name]
WHERE [condition];

Bạn có thể chọn các cột cụ thể bằng cách chỉ định chúng hoặc chọn tất cả các trường của bảng bằng cách sử dụng * ký tự đại diện. Theo tùy chọn, bạn có thể sáng tạo với nhiều điều kiện hơn bằng cách sử dụng WHERE bản tường trình.

Ở đây, chúng tôi chọn tất cả các hàng và tất cả các cột từ users cơ sở dữ liệu:

const query = `
SELECT *
FROM users
`;

Bây giờ, để chạy truy vấn này với cơ sở dữ liệu, chúng ta sẽ sử dụng client:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    for (let row of res.rows) {
        console.log(row);
    }
    client.end();
});

Chạy mã này sẽ mang lại:

{
email: '[email protected]',
firstname: 'john',
lastname: 'doe',
age: 21
}
{
email: '[email protected]',
firstname: 'anna',
lastname: 'dias',
age: 35
}

Truy vấn này trả về tất cả các người dùng được thêm vào cơ sở dữ liệu. Bạn cũng có thể lọc người dùng theo trường của họ.

Ví dụ: nếu chúng tôi muốn trả lại tất cả người dùng dưới 30 tuổi, chúng tôi sẽ thêm WHERE mệnh đề:

const query = `
SELECT *
FROM users
WHERE age<30
`;

Và sau đó, chúng tôi sẽ chạy nó trên cơ sở dữ liệu:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    for (let row of res.rows) {
        console.log(row);
    }
    client.end();
});

Chạy mã này sẽ mang lại:

{
email: '[email protected]',
firstname: 'john',
lastname: 'doe',
age: 21
}

Cập nhật dữ liệu

Để cập nhật dữ liệu đã tồn tại, chúng tôi có thể sử dụng UPDATE bản tường trình:

UPDATE [table_name]
SET [column1] = [value1], [column2] = [value2], ...
WHERE [condition];

Bạn có thể đặt từng giá trị cập nhật cho từng cột với SET. Sau WHERE bạn có thể xác định điều kiện mà các mục nhập cần được cập nhật.

Hãy điền truy vấn của chúng tôi:

const query = `
UPDATE users
SET age = 22
WHERE email="[email protected]"
`;

Bây giờ, hãy chạy truy vấn đối với cơ sở dữ liệu:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    if (err) {
        console.error(err);
        return;
    }
    console.log('Data update successful');
    client.end();
});

Chạy đoạn mã này sẽ cập nhật các mục nhập đáp ứng WHERE mệnh đề và in ra:

Data update successful

Để xác minh, hãy kiểm tra cơ sở dữ liệu của chúng tôi:

Xóa dữ liệu

Cuối cùng, để xóa dữ liệu, chúng ta có thể sử dụng DELETE bản tường trình:

DELETE FROM [table_name]
WHERE [condition];

Hãy cẩn thận với tuyên bố này vì bạn có thể vô tình xóa nhiều hơn những gì bạn muốn.

Hãy điền truy vấn của chúng tôi:

const query = `
DELETE FROM users
WHERE email="[email protected]"
`;

Và cuối cùng, chạy nó với cơ sở dữ liệu:

client.query(query, (err, res) => {
    if (err) {
        console.error(err);
        return;
    }
    if (err) {
        console.error(err);
        return;
    }
    console.log('Data delete successful');
    client.end();
});

Chạy mã này sẽ xóa mục nhập đáp ứng WHERE mệnh đề và in ra:

Data delete successful

Để xác minh, hãy xem cơ sở dữ liệu:

Gộp chung

Nếu ứng dụng của bạn đang sử dụng cơ sở dữ liệu thường xuyên, việc sử dụng một kết nối máy khách với cơ sở dữ liệu có thể sẽ làm chậm ứng dụng khi bạn có nhiều yêu cầu của người dùng. Cách dễ nhất và thuận tiện nhất để giải quyết vấn đề đó là sử dụng một nhóm kết nối.

Thông thường, khi một máy khách mới kết nối với cơ sở dữ liệu, quá trình thiết lập kết nối và xác thực mất khoảng 20-30 mili giây. Điều này rất quan trọng khi bạn đang chạy nhiều truy vấn hơn dẫn đến độ trễ vài giây, điều này có thể sẽ khiến người dùng cuối không hài lòng.

Ngoài ra, máy chủ PostgreSQL chỉ có thể xử lý một số lượng hạn chế máy khách tại một thời điểm nhất định, điều này sẽ phụ thuộc vào bộ nhớ máy chủ của bạn. Vì vậy, nếu 100 truy vấn diễn ra trong một giây – giới hạn này có thể làm hỏng máy chủ của bạn.

Ngoài ra, ứng dụng khách chỉ có thể xử lý một yêu cầu tại một thời điểm cho một kết nối duy nhất, điều này làm chậm mọi thứ hơn.

Trong tình huống như thế này, bạn có thể sử dụng pg-pool để giải quyết điều đó.

Tạo một nhóm

Đầu tiên nhập Pool Lớp học từ pg:

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

Sau đó, hãy tạo một đối tượng pool mới:

const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'testdb',
    password: '1234abcd',
    port: 5432,
});

Nếu bạn không định cấu hình tên người dùng, máy chủ lưu trữ và các thuộc tính khác, bạn phải xác định các biến môi trường cho các biến này trong tệp cấu hình. Nó khá giống như khi cấu hình một ứng dụng khách.

Tiếp theo, hãy xác định một trình xử lý lỗi cho nhóm. Nếu có bất kỳ lỗi nào xảy ra từ nhóm, lệnh gọi lại trong sự kiện này sẽ được kích hoạt:

pool.on('error', (err, client) => {
    console.error('Error:', err);
});

Điều này bao gồm chúng tôi trong trường hợp có lỗi mạng.

Sau đó, sử dụng poolchúng tôi kết nối với cơ sở dữ liệu và sử dụng client trong nhóm đó để thực hiện một truy vấn:

const query = `
SELECT *
FROM users
`;

pool.connect((err, client, done) => {
    if (err) throw err;
    client.query(query, (err, res) => {
        done();
        if (err) {
            console.log(err.stack);
        } else {
            for (let row of res.rows) {
                console.log(row);
            }
        }
    });
});

Điều này sẽ mang lại:

{
  email: '[email protected]',
  firstname: 'john',
  lastname: 'doe',
  age: 21
}
{
  email: '[email protected]',
  firstname: 'anna',
  lastname: 'dias',
  age: 35
}

Một lần nữa, sẽ hợp lý hơn khi sử dụng các lời hứa trong trường hợp này:

pool.connect()
    .then((client) => {
        client.query(query)
            .then(res => {
                for (let row of res.rows) {
                    console.log(row);
                }
            })
            .catch(err => {
                console.error(err);
            });
    })
    .catch(err => {
        console.error(err);
    });

Hoặc thậm chí async/await cú pháp:

(async () => {
    try {
        const client = await pool.connect();
        const res = await client.query(query);

        for (let row of res.rows) {
            console.log(row);
        }
    } catch (err) {
        console.error(err);
    }
})();

Sử dụng con trỏ để đọc các truy vấn lớn

Thông thường, dữ liệu nhận được từ một truy vấn được tải thẳng vào bộ nhớ. Tập dữ liệu càng lớn thì mức sử dụng bộ nhớ càng cao.

Vì vậy, khi bạn đang cố gắng truy vấn một tập dữ liệu lớn có thể chứa hàng nghìn bản ghi – việc tải tất cả vào bộ nhớ rất kém hiệu quả và đôi khi, điều đó rõ ràng là không thể. Con trỏ có thể giúp bạn trong trường hợp như thế này bằng cách truy xuất một số bản ghi giới hạn tại một thời điểm.

Theo một nghĩa nào đó, sử dụng con trỏ tương tự như truyền dữ liệu trực tuyến vì bạn sẽ truy cập nó tuần tự trong các khối nhỏ hơn. Để sử dụng con trỏ, chúng ta phải cài đặt pg-cursor Đầu tiên:

$ npm install --save pg pg-cursor

Chúng tôi sẽ vượt qua một new Cursor đến query(). Các cursor sẽ không thực sự truy xuất bất kỳ thông tin nào cho đến khi chúng tôi chỉ định giới hạn bằng cách sử dụng read() phương pháp:

const { Pool } = require('pg');
const Cursor = require('pg-cursor');

const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'testdb',
    password: '1234abcd',
    port: 5432,
});

(async () => {
    const client = await pool.connect();
    const query = 'SELECT * FROM users';

    const cursor = await client.query(new Cursor(query));

    cursor.read(1, (err, rows) => {
        console.log('We got the first row set');
        console.log(rows);

        cursor.read(1, (err, rows) => {
            console.log('This is the next row set');
            console.log(rows);
        });
    });
})();

Các cursor‘S read() phương thức cho phép chúng tôi xác định có bao nhiêu hàng mà chúng tôi muốn truy xuất từ ​​hiện tại cursor ví dụ. Trong ví dụ này để đơn giản, chúng tôi đã giới hạn các hàng cho một bản ghi. Sau đó, chúng tôi đã đọc một tập hợp các hàng khác sau đó.

Nếu bạn đã đến cuối các hàng trong cơ sở dữ liệu, rows mảng sẽ có chiều dài 0.

Sự kết luận

PostgreSQL là một cơ sở dữ liệu quan hệ nguồn mở, miễn phí, thực sự phổ biến. Các node-postgres là một mô-đun trưởng thành và được sử dụng rộng rãi làm cầu nối giữa Node.js với PostgreSQL.

Trong bài viết này, chúng tôi đã thiết lập cơ sở dữ liệu PostgreSQL và phát triển chức năng CRUD cơ bản thông qua một tập lệnh Node.js đơn giản. Sau đó, chúng tôi đã khám phá hỗ trợ gộp và việc sử dụng con trỏ để giới hạn dữ liệu được truy xuất.

Như mọi khi, mã nguồn có sẵn trên GitHub.