Интеграция PostgreSQL с Node.js и node-postgres

Введение В этой статье мы обсудим, как интегрировать PostgreSQL с Node.js. Чтобы лучше следовать этой статье, мы рекомендуем вам иметь опыт использования Node.js и операторов SQL. В этой статье мы будем использовать простой синтаксис javascript ES6. Есть несколько разных клиентов, которые вы можете использовать для интеграции PostgreSQL с Node.js. В этой статье мы будем использовать модуль node-postgres. Это популярный и развитый модуль по сравнению с другими клиентами PostgreSQL. Кроме того, лет

Вступление

В этой статье мы обсудим, как интегрировать PostgreSQL с Node.js.

Чтобы лучше следовать этой статье, мы рекомендуем вам иметь опыт использования Node.js и операторов SQL. В этой статье мы будем использовать простой синтаксис javascript ES6.

Есть несколько разных клиентов, которые вы можете использовать для интеграции PostgreSQL с Node.js. В этой статье мы будем использовать модуль node-postgres Это популярный и развитый модуль по сравнению с другими клиентами PostgreSQL.

Кроме того, вы также можете использовать PostgreSQL с ORM, например Sequelize . Но мы не будем использовать такой ORM-модуль в этой статье. Вместо этого мы будем использовать простые SQL-запросы, которые затем можно будет использовать для более сложных взаимодействий с базой данных.

PostgreSQL

PostgreSQL - популярная база данных SQL. Она активно разрабатывалась последние 30 с лишним лет и считается одной из самых передовых реляционных баз данных. PostgreSQL также прост в изучении и настройке по сравнению с другими доступными реляционными базами данных. Из-за того, что он бесплатный и имеет открытый исходный код, это популярный выбор среди стартапов.

PostgreSQL - это кроссплатформенная база данных, работающая во всех основных операционных системах. Однако конфигурация и доступ / создание базы данных немного различаются в зависимости от операционных систем.

В этой статье мы будем использовать Ubuntu 18.04, популярную платформу Linux, которая по умолчанию включает PostgreSQL. Некоторые шаги могут немного отличаться, если вы используете другую операционную систему.

Конфигурация проекта

Начнем с простого пустого проекта Node.js с настройками по умолчанию:

 $ npm init -y 

Затем давайте используем npm для установки node-postgres , который будет использоваться для подключения и взаимодействия с Postgres:

 $ npm install --save pg 

Реализация операций CRUD

После начальной загрузки нашего проекта давайте продолжим и настроим базу данных. После этого мы напишем некоторые базовые функции CRUD.

Конфигурация базы данных

Как и в случае со всеми реляционными базами данных, мы начнем с создания базы данных и подключения к ней. Для этого вы можете использовать интерфейс командной строки или клиент с графическим интерфейсом. Поскольку настроить через интерфейс командной строки очень просто, мы и будем этим заниматься.

Для Ubuntu команда psql по умолчанию войдет в интерфейс командной строки. PostgreSQL создаст пользователя с именем postgres для доступа к базе данных на платформах на базе Linux. Таким образом, мы можем использовать следующую команду для входа в систему как пользователь postgres

 $ sudo -i -u postgres 

Затем войдите в CLI, запустив:

 $ psql 

Вы должны увидеть командную оболочку, подобную этой:

команда postgresqlpsql{.ezlazyload}

Чтобы просмотреть текущие базы данных, воспользуемся командой \list или \l :

команда спискаpostgresql{.ezlazyload}

А теперь давайте создадим наш собственный с помощью SQL-запроса:

 CREATE DATABASE testdb; 

Выполняя эту команду, мы создаем testdb данных testdb и приветствуем вывод, подтверждающий нашу команду:

 CREATE DATABASE 

Поскольку база данных создана, теперь мы можем получить к ней доступ. Хотя PostgreSQL создает postgres умолчанию, пароль по умолчанию не установлен. Если вы хотите установить пароль (а не оставлять его пустым), используйте команду \password :

изменение пароля postgresql с помощьюcli{.ezlazyload}

Установив пароль, мы готовы использовать базу данных в нашем проекте.

Подключение к базе данных

У вас есть два варианта подключения к серверу PostgreSQL с помощью модуля node-postgres Один из вариантов - использовать одного клиента. Другой метод - использовать пул соединений. Однако, если ваше приложение очень часто использует базу данных, пул будет лучшим вариантом, чем использование одного клиента.

Подключиться к базе данных с помощью node-postgres можно двумя способами - с помощью одного клиента и с помощью пула подключений .

Позже в этой статье мы рассмотрим, как использовать пул соединений для подключения к базе данных. А пока давайте для краткости и простоты подключимся к базе данных с помощью одного клиента:

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

Здесь мы вручную настроили параметры. Однако вы можете подключиться к базе данных, не передавая ни один из них:

 const { Client } = require('pg'); 
 
 const client = new Client(); 
 client.connect(); 

Но опять же, Node нужно знать, как подключиться к базе данных, поэтому мы предоставим их через переменные среды:

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

Если вы не настраивали их самостоятельно, модуль будет использовать значения по умолчанию:

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

В Linux process.env.USER будет содержать значение для текущего пользователя, который вошел в систему.

Создание таблиц

Подготовив базу данных для вставки данных, давайте создадим несколько таблиц для хранения наших данных. Как и во всех базах данных на основе SQL, мы будем использовать запрос CREATE TABLE

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

Таблица состоит из столбцов , и каждый столбец имеет тип данных. Например, firstName будет иметь varchar в качестве типа данных, который представляет строку переменного размера.

Если вы хотите узнать больше о поддерживаемых типах данных, вдокументации PostgreSQL они перечислены.

При этом мы можем использовать этот запрос для создания таблицы в базе данных:

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

Чтобы фактически запустить этот запрос к базе данных, мы используем функцию query() client объекта, который мы настроили ранее:

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

Примечание. Не забудьте end() ваше соединение с клиентом после выполнения запроса.

Запуск этого кода создаст нашу таблицу и распечатает:

 Table is successfully created 

Этого также можно добиться с помощью обещаний и async/await . Поскольку вызов базы данных может завершиться ошибкой, имеет смысл использовать обещания:

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

Как вы можете видеть в примере, мы можем использовать блок finally, чтобы закрыть соединение с базой данных. Таким образом, даже если запрос выдал err , соединение будет закрыто.

В качестве альтернативы мы также можем использовать синтаксис async/await

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

Все эти подходы должны дать один и тот же результат:

 Table is successfully created 

Чтобы убедиться в этом, давайте воспользуемся psql для проверки БД. Откройте терминал, запустите оболочку с помощью psql и выберите базу данных с помощью команды \c [database] . \c - это сокращение от \connect :

 \c testdb 

testdb список таблиц в базе данных testdb, выполнив команду \dt :

таблицы списковpostgresql{.ezlazyload}

Вы также можете запросить конкретные таблицы, указав их имена:

 testdb=# \dt FOO 

Этот запрос отобразит таблицу с именем FOO .

Создание / вставка данных

Мы можем использовать оператор SQL INSERT INTO для вставки данных в таблицу:

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

Чтобы сделать этот запрос конкретным, давайте вставим наши собственные значения и построим запрос:

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

И наконец, давайте запустим запрос к базе данных:

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

Примечание. Как и в прошлый раз, эта функция может быть написана с использованием синтаксиса async/await Эти дополнительные примеры опущены для краткости.

Запуск этого кода вставит пользователя в нашу базу данных и распечатает:

 Data insert successful 

Чтобы убедиться в этом, в нашей testdb данных SELECT :

 SELECT * from users; 

Очевидно, мы видим, что пользователь действительно был успешно создан:

postgresql показывает данныетаблицы{.ezlazyload}

Получение / выбор данных

Для получения данных из базы данных используется SELECT :

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

Вы можете выбрать определенные столбцы, указав их, или выбрать все поля таблицы, используя подстановочный знак * При желании вы можете проявить творческий подход к большему количеству условных выражений, WHERE

Здесь мы выбираем все строки и все столбцы из базы данных users

 const query = ` 
 SELECT * 
 FROM users 
 `; 

Теперь, чтобы запустить этот запрос к базе данных, мы снова воспользуемся client :

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

Запуск этого кода даст:

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

Этот запрос возвращает всех пользователей, добавленных в базу данных. Вы также можете фильтровать пользователей по их полям.

Например, если бы мы хотели вернуть всех пользователей младше 30 лет, мы бы добавили предложение WHERE :

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

А затем мы запускали его в базе данных:

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

Запуск этого кода даст:

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

Обновление данных

Чтобы обновить уже существующие данные, мы можем использовать оператор UPDATE

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

Вы можете установить каждое обновленное значение для каждого столбца с помощью ключевого слова SET После WHERE вы можете определить условие, при котором записи должны быть обновлены.

Давайте заполним наш запрос:

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

Теперь давайте запустим запрос к базе данных:

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

Запуск этого фрагмента кода обновит записи, удовлетворяющие WHERE , и распечатает:

 Data update successful 

Для проверки давайте проверим нашу базу данных:

postgresql показывает данныетаблицы{.ezlazyload}

Удаление данных

Наконец, чтобы удалить данные, мы можем использовать оператор DELETE

 DELETE FROM [table_name] 
 WHERE [condition]; 

Будьте осторожны с этим утверждением, так как вы можете случайно удалить больше, чем хотите.

Давайте заполним наш запрос:

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

И, наконец, запустим его в базе данных:

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

Запуск этого кода удалит запись, удовлетворяющую WHERE , и распечатает:

 Data delete successful 

Для проверки взглянем на базу данных:

postgresql показывает данныетаблицы{.ezlazyload}

Объединение

Если ваше приложение часто использует базу данных, использование одного клиентского подключения к базе данных, скорее всего, замедлит работу приложения, когда у вас будет много пользовательских запросов. Самый простой и удобный способ решить эту проблему - использовать пул соединений.

Обычно, когда новый клиент подключается к базе данных, процесс установления соединения и аутентификации занимает около 20-30 миллисекунд. Это важно, когда вы выполняете больше запросов, что приводит к задержке в несколько секунд, что, вероятно, приведет к неудовлетворительному опыту конечного пользователя.

Кроме того, сервер PostgreSQL может одновременно обрабатывать только ограниченное количество клиентов, которое будет зависеть от памяти вашего сервера. Таким образом, если за секунду выполняется 100 запросов, это ограничение может привести к сбою вашего сервера.

Кроме того, клиент может обрабатывать только один запрос за раз для одного соединения, что еще больше замедляет работу.

В такой ситуации вы можете использовать pg-pool чтобы решить эту проблему.

Создание пула

Сначала импортируйте класс Pool из модуля pg

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

Затем давайте создадим новый объект пула:

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

Если вы не настроите имя пользователя, хост и другие свойства, вам придется определить для них переменные среды в файле конфигурации. Это почти то же самое, что и при настройке одного клиента.

Затем давайте определим обработчик ошибок для пула. Если есть какие-либо ошибки, выбрасываемые из пула, обратный вызов в этом событии будет запущен:

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

Это покрывает нас в случае сетевой ошибки.

Затем, используя pool , мы подключаемся к базе данных и используем client в этом пуле для выполнения запроса:

 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); 
 } 
 } 
 }); 
 }); 

Это должно дать:

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

Опять же, в этом случае имеет смысл использовать обещания:

 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); 
 }); 

Или даже синтаксис async/await

 (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); 
 } 
 })(); 

Использование курсора для чтения больших запросов

Обычно данные, полученные в результате запроса, загружаются прямо в память. Чем больше набор данных, тем выше будет использование памяти.

Поэтому, когда вы пытаетесь запросить большой набор данных, который может содержать, как правило, тысячи записей, загружать все это в память крайне неэффективно, а зачастую это просто невозможно. Курсор может помочь вам в подобной ситуации, извлекая ограниченное количество записей за раз.

В некотором смысле использование курсора похоже на потоковую передачу данных, поскольку вы будете обращаться к ним последовательно небольшими блоками. Чтобы использовать курсор, мы должны сначала pg-cursor

 $ npm install --save pg pg-cursor 

Мы передадим new Cursor в функцию query() cursor не получит никакой информации, пока мы не укажем ограничение с помощью метода read()

 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); 
 }); 
 }); 
 })(); 

Метод cursor read() позволяет нам определить, сколько строк мы хотим получить из текущего экземпляра cursor В этом примере для простоты мы ограничили строки для одной записи. После этого мы прочитали еще один набор строк.

Если вы достигли конца строк в базе данных, rows будет иметь длину 0 .

Заключение

PostgreSQL - действительно популярная бесплатная реляционная база данных с открытым исходным кодом. Модуль node-postgres - это широко используемый и развитый модуль, который связывает Node.js с PostgreSQL.

В этой статье мы создали базу данных PostgreSQL и разработали базовые функции CRUD с помощью простого скрипта Node.js. Затем мы изучили поддержку пула и использование курсоров для ограничения извлекаемых данных.

Как всегда, исходный код доступен на GitHub .

comments powered by Disqus