Agregar una base de datos PostgreSQL a una aplicaci贸n Node.js en Heroku

    Introducci贸n

    Heroku es un servicio de alojamiento que admite aplicaciones Node.js. Es f谩cil de usar y su funcionalidad se puede ampliar con plugins. Hay plugins para varias cosas, que incluyen mensajer铆a / colas, registro, m茅tricas y, por supuesto, almacenes de datos. Los plugins del almac茅n de datos admiten bases de datos populares, como PostgreSQL, Redis y DynamoDB.

    En este tutorial, agregaremos una base de datos PostgreSQL a una aplicaci贸n de node que acorta las URL. Luego, implementaremos la aplicaci贸n en Heroku y configuraremos el plugin PostgreSQL.

    PostgreSQL

    Si a煤n no lo tiene, deber谩 instalar Postgres en su m谩quina. Hay algunas formas diferentes de instalarlo, dependiendo de su sistema operativo. Visita el P谩gina de descargas de PostgreSQL para m谩s informaci贸n.

    Con PostgreSQL instalado, podemos crear una base de datos para que la aplicaci贸n de acortador de URL use:

    $ psql
    psql (11.6)
    Type "help" for help.
    
    tomkadwill=#
    

    Y luego usa el CREATE DATABASE Comando SQL:

    tomkadwill=# CREATE DATABASE urlshortener_development;
    CREATE DATABASE
    tomkadwill=# l
                                             List of databases
                Name          |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
    --------------------------+------------+----------+-------------+-------------+-----------------------
     urlshortener_development | tomkadwill | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
    

    Aqu铆 creamos una base de datos llamada urlshortener_development y luego usa l para imprimir una lista de todas las bases de datos PostgreSQL en el sistema.

    Nuestra nueva base de datos urlshortener_development 驴Est谩 ah铆 para que sepamos que se cre贸 correctamente? Adem谩s, tenga en cuenta el propietario de la base de datos porque lo necesitaremos m谩s adelante (el suyo ser谩 diferente al m铆o).

    Integraci贸n de Postgres en una aplicaci贸n de node

    La aplicaci贸n Node en la que trabajaremos es bastante simple. Si desea construirlo desde cero, puede seguir nuestra gu铆a, Implementar una aplicaci贸n Node.js en Heroku, o puede descargarlo desde GitHub.

    La l贸gica de la aplicaci贸n Express est谩 dentro app.js:

    const express = require('express');
    const app = express();
    const path = require('path');
    const port = process.env.PORT || 3000;
    const urlShortener = require('node-url-shortener');
    
    const bodyParser = require('body-parser')
    app.use(bodyParser.urlencoded({extended: true}));
    app.use(express.urlencoded());
    
    app.get("https://Pharos.sh.com/", function(req, res) {
      res.sendFile(path.join(__dirname + '/index.html'));
    });
    
    app.post('/url', function(req, res) {
      const url = req.body.url
    
      urlShortener.short(url, function(err, shortUrl){
        res.send(shortUrl);
      });
    });
    
    app.listen(port, () => console.log(`url-shortener listening on port ${port}!`));
    

    Puede ejecutar la aplicaci贸n a trav茅s de npm start. Una vez iniciado, busque localhost: 3000 y deber铆a ver la p谩gina de inicio:

    El plan es actualizar app.js para que almacene cada URL y URL acortada en una tabla de base de datos y luego muestre los 煤ltimos 5 resultados en la interfaz de usuario.

    Lo primero que debemos hacer es instalar un ORM (Object Relation Mapper) biblioteca. Interactuar directamente con PostgreSQL es dif铆cil porque tendr铆amos que escribir nuestras propias consultas SQL sin procesar.

    Un ORM nos permite interactuar con la base de datos a trav茅s de llamadas API m谩s simples. Tenga en cuenta que existen algunas desventajas en el uso de ORM, pero no las cubrir茅 en este tutorial.

    Hay varias bibliotecas ORM diferentes para Node, en este caso usaremos Secuela:

    $ npm install --save sequelize
    $ npm install --save pg pg-hstore
    

    El primer comando instala Sequelize y el segundo instala el controlador PostgreSQL para Node. Sequelize admite varias bases de datos, por lo que debemos especificar cu谩l usar y proporcionar el controlador de node.

    Migraciones

    Con Sequelize instalado y configurado, podemos pensar en la estructura de la base de datos. Solo necesitamos algo simple, una sola tabla con 3 columnas: una ID 煤nica, una URL original y una URL abreviada.

    Podr铆amos crear la nueva tabla de base de datos manualmente, pero eso har铆a que las implementaciones fueran dolorosas. Tendr铆amos que recordar nuestras consultas y ejecutarlas en cada entorno.

    Una mejor manera de manejar los cambios de la base de datos es a trav茅s de las migraciones, que es donde los cambios de la base de datos se codifican dentro de la aplicaci贸n. Afortunadamente, Sequelize admite migraciones listas para usar. Escribamos una migraci贸n para crear una tabla de URL.

    Primero, instalaremos la CLI de Sequelize, que nos permite ejecutar migraciones:

    $ npm install --save sequelize-cli
    

    A continuaci贸n, inicializaremos Sequelize:

    $ npx sequelize-cli init
    

    Esto crear谩 un config/config.json archivo y models, migrationsy seeders directorios.

    Despu茅s de eso, necesitamos modificar el config.json archivo para que pueda conectarse a nuestra base de datos PostgreSQL:

    {
      "development": {
        "username": "tomkadwill",
        "password": "password",
        "database": "urlshortener_development",
        "host": "localhost",
        "dialect": "postgres",
        "operatorsAliases": false
      }
    }
    

    Una vez que el archivo est茅 listo, generemos la migraci贸n usando la CLI de Sequelize. Aqu铆, definiremos nuestros campos a trav茅s del attributes bandera. No incluiremos el id campo ya que se agrega autom谩ticamente:

    $ npx sequelize-cli model:generate --name Url --attributes url:string,shortUrl:string
    

    Esto crear谩 un archivo de migraci贸n que se parece a esto:

    'use strict';
    module.exports = {
      up: (queryInterface, Sequelize) => {
        return queryInterface.createTable('Urls', {
          id: {
            allowNull: false,
            autoIncrement: true,
            primaryKey: true,
            type: Sequelize.INTEGER
          },
          url: {
            type: Sequelize.STRING
          },
          shortUrl: {
            type: Sequelize.STRING
          },
          createdAt: {
            allowNull: false,
            type: Sequelize.DATE
          },
          updatedAt: {
            allowNull: false,
            type: Sequelize.DATE
          }
        });
      },
      down: (queryInterface, Sequelize) => {
        return queryInterface.dropTable('Urls');
      }
    };
    

    Las migraciones contienen un up y down funci贸n. up se utiliza para hacer avanzar la base de datos y down se utiliza para retroceder.

    En este caso up crea un Urls tabla que tiene 5 campos. Tiene el url y shortUrl campos, as铆 como id, createdAty updatedAt, que se agregan de forma predeterminada.

    los down la migraci贸n simplemente dejar谩 caer el Urls mesa.

    Finalmente, ejecutemos la migraci贸n:

    $ npx sequelize-cli db:migrate
    

    Una vez que se haya ejecutado, podemos consultar la base de datos directamente para verificar que todo funcion贸:

    $ psql -p 5432 "urlshortener_development"
    psql (11.6)
    Type "help" for help.
    
    urlshortener_development=# dt
                  List of relations
     Schema |     Name      | Type  |   Owner
    --------+---------------+-------+------------
     public | SequelizeMeta | table | tomkadwill
     public | Urls          | table | tomkadwill
    (2 rows)
    
    urlshortener_development=# d "Urls"
                                           Table "public.Urls"
      Column   |           Type           | Collation | Nullable |              Default
    -----------+--------------------------+-----------+----------+------------------------------------
     id        | integer                  |           | not null | nextval('"Urls_id_seq"'::regclass)
     url       | character varying(255)   |           |          |
     shortUrl  | character varying(255)   |           |          |
     createdAt | timestamp with time zone |           | not null |
     updatedAt | timestamp with time zone |           | not null |
    Indexes:
        "Urls_pkey" PRIMARY KEY, btree (id)
    

    Como puede ver, hay dos tablas de base de datos: SequelizeMeta y Urls. Y si inspeccionamos Urls, los campos esperados est谩n ah铆.

    Guardando URL

    Ahora que tenemos PostgreSQL configurado y hemos creado una tabla de base de datos con la estructura correcta, el siguiente paso es actualizar nuestra aplicaci贸n para que persista las URL en la base de datos. Recordar que npx sequelize-cli model:generate cre贸 un archivo de modelo, lo usaremos para guardar las URL en la base de datos.

    Primero necesitamos importar los modelos a app.js, al requerir models/index.js:

    const db = require('./models/index.js');
    

    los models/index.js El archivo fue generado por Sequelize y su prop贸sito es incluir todos los archivos del modelo.

    A continuaci贸n, necesitamos actualizar el post ruta para que cree un registro de base de datos. Podemos usar el findOrCreate() funci贸n para que cada URL solo tenga una entrada de base de datos 煤nica:

    app.post('/url', function(req, res) {
      const url = req.body.url
    
      urlShortener.short(url, function(err, shortUrl) {
        db.Url.findOrCreate({where: {url: url, shortUrl: shortUrl}})
        .then(([urlObj, created]) => {
          res.send(shortUrl)
        });
      });
    });
    

    Cuando db.Url.findOrCreate() se llama, intentar谩 encontrar un registro que coincida con el proporcionado por el usuario url y generado shortUrl. Si se encuentra uno, Sequelize no hace nada; de lo contrario, crea un nuevo registro.

    Visualizaci贸n de URL en la interfaz de usuario

    Para facilitar el uso, actualice la aplicaci贸n para mostrar las 煤ltimas 5 URL persistentes.

    Con ese fin, agregaremos un motor de plantilla para que Express pueda representar las URL de forma din谩mica. Hay muchos motores de plantilla disponibles, pero en este caso usaremos Manillares Express:

    $ npm install --save express-handlebars
    

    Despu茅s de instalar el paquete, podemos agregarlo a app.js:

    const exphbs = require('express-handlebars');
    
    app.engine('handlebars', exphbs());
    app.set('view engine', 'handlebars');
    

    A continuaci贸n, necesitamos cambiar la estructura del directorio de la aplicaci贸n. express-handlebars asume que las vistas est谩n ubicadas en un views directorio. Tambi茅n asume que tenemos un views/layouts/main.handlebars archivo:

    .
    鈹溾攢鈹 app.js
    鈹斺攢鈹 views
        鈹溾攢鈹 index.handlebars
        鈹斺攢鈹 layouts
            鈹斺攢鈹 main.handlebars
    

    Entonces, mov谩monos y cambiemos el nombre index.html archivo:

    $ mv index.html views/index.handlebars
    

    Y finalmente, hagamos un archivo de dise帽o, views/layouts/main.handlebars:

    <html>
    <head>
        <title>Url Shortener</title>
    </head>
    <body>
        {{{body}}}
    </body>
    </html>
    

    Hay un cierto orden en el que se cargan los archivos de plantilla: express-handlebars rendir谩 views/layouts/main.handlebars que luego rinde views/index.handlebars dentro de {{{body}}} etiqueta.

    Ahora que tenemos la estructura de directorio correcta, agreguemos c贸digo HTML a index.handlebars para mostrar din谩micamente las URL:

    <ul>
      {{#each urlObjs}}
      <li>{{this.url}} -- <b>{{this.shortUrl}}</b></li>
      {{/each}}
    </ul>
    

    Aqu铆 usamos Handlebars ‘ each helper para iterar sobre cada objeto URL y mostrar la URL original y la URL corta.

    Lo 煤ltimo que debemos hacer es actualizar el GET ruta en app.js para pasar las URL a la vista:

    app.get("https://Pharos.sh.com/", function(req, res) {
      db.Url.findAll({order: [['createdAt', 'DESC']], limit: 5})
      .then(urlObjs => {
        res.render('index', {
          urlObjs: urlObjs
        });
      });
    });
    

    Repasemos lo que est谩 sucediendo aqu铆. Cuando / se solicita, Sequelize consulta el Urls mesa. La consulta est谩 ordenada por createdAt y limitado a 5, lo que garantiza que solo se devuelvan los 5 resultados m谩s recientes.

    El resultado de la consulta se pasa a res.render como la variable local urlObjs, que ser谩 utilizado por la plantilla.

    La p谩gina renderizada se ve as铆:

    Si tiene alg煤n problema, puede descargar el c贸digo completo desde GitHub.

    Implementar la aplicaci贸n en Heroku

    Ahora que tenemos la aplicaci贸n ejecut谩ndose localmente, esta secci贸n cubrir谩 c贸mo hacer que se ejecute en Heroku y c贸mo conectar la base de datos una vez que se est茅 ejecutando.

    Primero, implementemos todos nuestros cambios en Heroku:

    $ git push heroku master
    

    Consulte Implementaci贸n de una aplicaci贸n Node.js en Heroku para obtener una gu铆a detallada sobre la implementaci贸n en Heroku.

    Conectando la base de datos

    Agregar una base de datos no es dif铆cil y todo lo que requiere es una sola l铆nea de comando:

    $ heroku addons:create heroku-postgresql:hobby-dev
    

    Este comando crea el plugin PostgreSQL para Heroku y establece una variable de entorno llamada DATABASE_URL – solo necesitamos decirle a Sequelize que lo use actualizando el archivo de configuraci贸n:

    {
      "development": {
        "username": "tomkadwill",
        "password": "password",
        "database": "urlshortener_development",
        "host": "localhost",
        "dialect": "postgres",
        "operatorsAliases": false
      },
      "production": {
        "username": "tomkadwill",
        "password": "password",
        "database": "urlshortener_production",
        "host": "localhost",
        "dialect": "postgres",
        "operatorsAliases": false,
        "use_env_variable": "DATABASE_URL"
      }
    }
    

    Aqu铆 hemos agregado una nueva secci贸n de configuraci贸n para producci贸n, es lo mismo que desarrollo, excepto por el nombre de la base de datos y use_env_variable campo.

    Secuela de usos use_env_variable para obtener el nombre de la variable de entorno que se utilizar谩 para conectarse a la base de datos.

    Heroku tiene NODE_ENV establecido en “producci贸n” de forma predeterminada, lo que significa que buscar谩 las configuraciones de producci贸n. Compromet谩monos y empujemos a Heroku.

    A continuaci贸n, debemos ejecutar las migraciones en Heroku usando sequelize db:migrate:

    $ heroku run bash
    Running bash on 猬 nameful-wolf-12818... up, run.5074 (Free)
    ~ $ sequelize db:migrate
    

    Si no hemos creado la migraci贸n antes, ahora estar铆amos ejecutando scripts manualmente.

    En este punto, la aplicaci贸n deber铆a estar funcionando en el navegador.

    Administrar la base de datos de Heroku

    Probablemente necesitar谩 consultar o modificar su base de datos de producci贸n en alg煤n momento. Por ejemplo, es posible que deba consultar algunos datos para ayudar a diagnosticar un error o puede que necesite modificar algunos datos del usuario. Veamos c贸mo hacer eso.

    Ejecuci贸n de tareas de migraci贸n de Sequelize

    Lo primero que debe saber es c贸mo ejecutar migraciones de Sequelize en Heroku. As铆 es como ver una lista de comandos disponibles:

    $ heroku run bash
    Running bash on 猬 nameful-wolf-12818... up, run.1435 (Free)
    ~ $ sequelize
    

    Estos son algunos de los m谩s 煤tiles:

    • sequelize db:migrate:undo: Deshacer una 煤nica migraci贸n
    • sequelize db:migrate:undo:all: Revertir todas las migraciones. Volver efectivamente a una base de datos limpia
    • sequelize db:migrate:status: Compruebe en qu茅 migraci贸n se encuentra su aplicaci贸n
    Uso de modelos de Sequelize en la consola

    Adem谩s de las tareas de CLI, podemos usar modelos de Sequelize directamente en la consola de Node:

    $ heroku run bash
    ~ $ node
    Welcome to Node.js v12.14.0.
    Type ".help" for more information.
    >
    

    A continuaci贸n, se muestran algunos ejemplos de c贸digo que se pueden ejecutar en la consola de Node. Puede notar que se parecen al Node REPL.

    Consulta de una URL individual por ID:

    db.Url.findByPk(1).then(url => {
      console.log(
        url.get({plain: true})
      );
    });
    

    Consultando todas las URL:

    db.Url.findAll().then(urls => {
      urls.map(url => {
        console.log(
          url.get({plain: true})
        );
      });
    });
    

    Insertar un registro de URL:

    db.Url.create({url: 'https://Pharos.sh.com/deploying-a-node-js-app-to-heroku', shortUrl: 'https://is.gd/56bEH3'});
    

    Consultar una URL por ID y actualizarla:

    db.Url.findByPk(1).then(url => {
      url.shortUrl="example.com";
      url.save();
    });
    

    Consultar una URL por ID y eliminarla:

    db.Url.findByPk(1).then(url => {
      url.destroy();
    });
    

    Conclusi贸n

    Existen numerosos plugins que se pueden utilizar para ampliar Heroku. Uno de esos plugins es Heroku Postgres, que le permite configurar f谩cilmente una base de datos para almacenar datos de aplicaciones.

    Hemos extendido una aplicaci贸n Node and Express simple para que almacene URL en una base de datos de Postgres, en Heroku.

    Etiquetas:

    Deja una respuesta

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