Usando PostgreSQL con Node.js y node-postgres

    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.

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *