Usando PostgreSQL con Node.js y node-postgres

U

Introducción

En este artículo, discutiremos cómo integrar PostgreSQL con Node.js.

Para seguir mejor este artículo, le recomendamos que tenga experiencia previa en el uso de Node.js y declaraciones SQL. Usaremos una sintaxis simple de JavaScript ES6 en este artículo.

Hay algunos clientes diferentes que puede utilizar para integrar PostgreSQL con Node.js. En este artículo, usaremos el node-postgres módulo. Es un módulo popular y maduro en comparación con otros clientes de PostgreSQL.

Además, puede usar PostgreSQL con un ORM como Secuela también. Pero no usaremos el uso de dicho módulo ORM en este artículo. En su lugar, usaremos consultas SQL simples, que luego puede desarrollar para interacciones de bases de datos más complejas.

PostgreSQL

PostgreSQL es una popular base de datos SQL. Ha estado en desarrollo activo durante los últimos 30 años y se considera una de las bases de datos relacionales más avanzadas que existen. PostgreSQL también es fácil de aprender y configurar en comparación con otras bases de datos relacionales disponibles. Debido a su naturaleza gratuita y de código abierto, esta es una opción popular entre las nuevas empresas.

PostgreSQL es una base de datos multiplataforma que se ejecuta en los principales sistemas operativos. Sin embargo, la configuración y el acceso / creación de la base de datos difiere ligeramente entre los sistemas operativos.

En este artículo, usaremos Ubuntu 18.04, que es una plataforma Linux popular e incluye PostgreSQL de forma predeterminada. Algunos pasos pueden ser un poco diferentes si está utilizando un sistema operativo diferente.

Configuración del proyecto

Comencemos con un proyecto Node.js en blanco simple con la configuración predeterminada:

$ npm init -y

Entonces, usemos npm para instalar el node-postgres módulo, que se utilizará para conectarse e interactuar con Postgres:

$ npm install --save pg

Implementación de operaciones CRUD

Con nuestro proyecto arrancado, continuemos y configuremos la base de datos. Después de eso, escribiremos algunas funciones básicas de CRUD.

Configuración de la base de datos

Al igual que con todas las bases de datos relacionales, comenzaremos creando una y conectándonos a ella. Puede utilizar la CLI o un cliente basado en GUI para hacer esto. Dado que es muy sencillo de configurar a través de la CLI, lo haremos.

Para Ubuntu, el valor predeterminado psql El comando ingresará a la CLI. PostgreSQL creará un usuario llamado postgres para acceder a la base de datos en plataformas basadas en Linux. Por lo tanto, podemos usar el siguiente comando para iniciar sesión como postgres usuario:

$ sudo -i -u postgres

Luego ingrese a la CLI ejecutando:

$ psql

Debería ver un shell de comandos similar a este:

Para ver las bases de datos actualmente presentes, usemos el list o l mando:

Y ahora, creemos el nuestro usando una consulta SQL:

CREATE DATABASE testdb;

Al ejecutar este comando, estamos creando un testdb base de datos y saludado con la salida, confirmando nuestro comando:

CREATE DATABASE

Desde que se creó la base de datos, ahora podemos acceder a ella. Mientras PostgreSQL crea un valor predeterminado postgres usuario, la contraseña no está configurada de forma predeterminada. Si desea establecer su contraseña (en lugar de dejarla en blanco), use el password mando:

Con su contraseña establecida, estamos listos para usar la base de datos en nuestro proyecto.

Conectarse a la base de datos

Tiene dos opciones que puede conectar a un servidor PostgreSQL con el node-postgres módulo. Una de las opciones es utilizar un solo cliente. El otro método consiste en utilizar un grupo de conexiones. Sin embargo, si su aplicación usa la base de datos con mucha frecuencia, el grupo será una mejor opción que usar un solo cliente.

Conectarse a la base de datos usando el node-postgres El módulo se puede hacer de dos maneras: usando un solo cliente y usando un grupo de conexiones.

Veremos cómo usar un grupo de conexiones para conectarse a la base de datos más adelante en este artículo. Por el momento, conectemos a la base de datos usando un solo cliente para mayor brevedad y simplicidad:

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

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

client.connect();

Aquí, hemos configurado manualmente las opciones. Sin embargo, puede conectarse a la base de datos sin pasar ninguno de esos:

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

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

Pero, de nuevo, Node necesita saber cómo conectarse a la base de datos, por lo que los proporcionaremos a través de variables de entorno:

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

Si no los ha configurado usted mismo, el módulo utilizará los valores predeterminados:

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

En Linux, el process.env.USER mantendrá el valor para el usuario actual que está conectado.

Crear tablas

Con la base de datos preparada para la inserción de datos, creemos algunas tablas para almacenar nuestros datos. Como con todas las bases de datos basadas en SQL, usaremos el CREATE TABLE consulta:

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

Una tabla consta de columnas y cada columna tiene un tipo de datos. Por ejemplo, un firstName la columna tendría varchar como el tipo de datos, que representa una Cadena de tamaño variable.

Si desea obtener más información sobre los tipos de datos admitidos, Documentación de PostgreSQL los enumera muy bien.

Dicho esto, podemos usar esta consulta para crear una tabla en la base de datos:

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

Para ejecutar esta consulta en la base de datos, usamos el query() función de la client objeto que configuramos antes:

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

Nota: No te olvides de end() su conexión con el cliente después de ejecutar la consulta.

Ejecutar este código creará nuestra tabla e imprimirá:

Table is successfully created

Esto también se puede lograr mediante promesas y async/await. Dado que una llamada a la base de datos puede fallar, tiene más sentido usar promesas:

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

Como puede ver en el ejemplo, podemos usar el bloque finalmente para cerrar la conexión con la base de datos. Entonces, incluso si la consulta arrojó un err, La conexión será cerrada.

Alternativamente, podemos usar el async/await sintaxis también:

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

Todos estos enfoques deberían producir el mismo resultado:

Table is successfully created

Para verificar esto, usemos el psql interfaz de línea de comandos para inspeccionar la base de datos. Abra una terminal, inicie el shell con psqly seleccione la base de datos con el c [database] mando. c es una abreviatura de connect:

c testdb

Entonces puede enumerar las tablas en la base de datos testdb ejecutando el dt mando:

También puede consultar tablas específicas proporcionando sus nombres:

testdb=# dt FOO

Esta consulta mostrará la tabla llamada FOO.

Creación / inserción de datos

Podemos usar el SQL INSERT INTO declaración para insertar datos en una tabla:

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

Para que esta consulta sea concreta, insertemos nuestros propios valores y construyamos una consulta:

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

Y finalmente, ejecutemos la consulta en la base de datos:

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

Nota: Al igual que la última vez, esta función se puede escribir utilizando el async/await sintaxis. Estos ejemplos adicionales se omiten por brevedad.

Ejecutar este código insertará un usuario en nuestra base de datos e imprimirá:

Data insert successful

Para verificar esto, en nuestro testdb base de datos, ejecute el SELECT declaración:

SELECT * from users;

Obviamente, podemos ver que el usuario se creó con éxito:

Recuperar / Seleccionar datos

Para recuperar datos de la base de datos, el SELECT se utiliza la declaración:

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

Puede seleccionar columnas específicas especificándolas o seleccionar todos los campos de una tabla usando el * comodín. Opcionalmente, puede ser creativo con más condicionales usando el WHERE declaración.

Aquí seleccionamos todas las filas y todas las columnas de la users base de datos:

const query = `
SELECT *
FROM users
`;

Ahora, para ejecutar esta consulta en la base de datos, usaremos el client otra vez:

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

Ejecutar este código producirá:

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

Esta consulta devuelve todos los usuarios agregados a la base de datos. También puede filtrar los usuarios por sus campos.

Por ejemplo, si quisiéramos devolver a todos los usuarios menores de 30 años, agregaríamos un WHERE cláusula:

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

Y luego, lo ejecutaríamos contra la base de datos:

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

Ejecutar este código producirá:

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

Actualización de datos

Para actualizar datos que ya existen, podemos usar el UPDATE declaración:

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

Puede establecer cada valor actualizado para cada columna con el SET palabra clave. Después de la WHERE cláusula, puede definir la condición de qué entradas deben actualizarse.

Completemos nuestra consulta:

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

Ahora, ejecutemos la consulta en la base de datos:

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

La ejecución de este código actualizará las entradas que satisfacen las WHERE cláusula e imprimir:

Data update successful

Para verificar, revisemos nuestra base de datos:

Eliminar datos

Finalmente, para borrar datos, podemos usar el DELETE declaración:

DELETE FROM [table_name]
WHERE [condition];

Tenga cuidado con esta declaración, ya que podría eliminar accidentalmente más de lo que desea.

Completemos nuestra consulta:

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

Y finalmente, ejecútelo contra la base de datos:

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

Ejecutar este código eliminará la entrada que cumple con WHERE cláusula e imprimir:

Data delete successful

Para verificar, echemos un vistazo a la base de datos:

Agrupación

Si su aplicación utiliza la base de datos con frecuencia, el uso de una única conexión de cliente a la base de datos probablemente ralentizará la aplicación cuando tenga muchas solicitudes de usuario. La forma más fácil y conveniente de abordar ese problema es utilizar un grupo de conexiones.

Por lo general, cuando un nuevo cliente se conecta a la base de datos, el proceso de establecer una conexión y autenticarse toma alrededor de 20 a 30 milisegundos. Esto es significativo cuando está ejecutando más consultas que provocan segundos de retraso, lo que probablemente terminará siendo una experiencia insatisfactoria para el usuario final.

Además, el servidor PostgreSQL solo puede manejar un número limitado de clientes en un momento dado, lo que dependerá de la memoria de su servidor. Entonces, si se realizan 100 consultas en un segundo, esta limitación podría bloquear su servidor.

Además, el cliente puede procesar solo una solicitud a la vez para una sola conexión, lo que ralentiza aún más las cosas.

En una situación como esta, puede utilizar el pg-pool módulo para resolver eso.

Crear una piscina

Primero importe el Pool Clase de la pg módulo:

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

Luego, creemos un nuevo objeto de grupo:

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

Si no configura el nombre de usuario, el host y otras propiedades, tendrá que definir las variables de entorno para estos en un archivo de configuración. Es más o menos lo mismo que cuando se configura un solo cliente.

A continuación, definamos un controlador de errores para el grupo. Si hay algún error arrojado desde el grupo, se disparará la devolución de llamada en este evento:

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

Esto nos cubre en caso de un error de red.

Luego, usando el pool objeto, nos conectamos a la base de datos y usamos un client en ese grupo para ejecutar una consulta:

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

Esto debería producir:

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

Nuevamente, tiene más sentido usar promesas en este caso:

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

O incluso el async/await sintaxis:

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

Usar el cursor para leer consultas grandes

Por lo general, los datos recibidos de una consulta se cargan directamente en la memoria. Cuanto mayor sea el conjunto de datos, mayor será el uso de memoria.

Entonces, cuando intenta consultar un conjunto de datos grande que puede contener miles de registros, es muy ineficiente cargarlo todo en la memoria y, a menudo, es simplemente imposible. Un cursor puede ayudarlo en una situación como esta al recuperar un número limitado de registros a la vez.

En cierto sentido, usar un cursor es similar a transmitir datos, ya que accederá a ellos secuencialmente en bloques más pequeños. Para usar el cursor, tenemos que instalar el pg-cursor módulo primero:

$ npm install --save pg pg-cursor

Pasaremos un new Cursor al query() función. los cursor no recuperará ninguna información hasta que especifiquemos el límite usando el read() método:

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

los cursores read() El método nos permite definir cuántas filas queremos recuperar del actual cursor ejemplo. En este ejemplo para simplificar, hemos limitado las filas para un registro. Luego, hemos leído otro conjunto de filas después de eso.

Si ha llegado al final de las filas de la base de datos, el rows la matriz será de longitud 0.

Conclusión

PostgreSQL es una base de datos relacional de código abierto, gratuita y muy popular. los node-postgres module es un módulo maduro y ampliamente utilizado que une Node.js con PostgreSQL.

En este artículo, configuramos una base de datos PostgreSQL y desarrollamos la funcionalidad CRUD básica a través de un simple script Node.js. Luego, hemos explorado la compatibilidad con la agrupación y el uso de cursores para limitar los datos recuperados.

Como siempre, el código fuente está disponible en GitHub.

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad