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

A

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.

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