Cómo usar un ORM en Node.js

logo node.js

En este blog hablaré un poco sobre los ORM’s, sus beneficios (pros) y limitaciones (cons), que es Sequelize y cómo usarlo.

Object-Relational Mapping

Es una técnica para convertir datos entre el sistema de tipos del lenguaje de programación y la base de datos. Como su nombre lo indica, esto va dirigido solamente a las base de datos relacional (SQL). Esto crea un efecto “objeto base de datos virtual” sobre la base de datos relacional, este efecto es lo que nos permite manipular la base de datos a través del código.

Object – Hace referencia al/los objeto(s) que podemos usar en nuestro lenguaje.

Relational – Hace referencia a nuestro Sistema Gestor de Base de Datos (MySQL, MSSQL, PostgreSQL).

Mapping – Hace referencia a la conexión entre el los objetos y las tablas.

 

tl;dr ORM es una técnica que nos permite hacer queries y manipular datos de la base de datos desde un lenguaje de programación.

 

Pros

  • Abstracto: Diseño de una estructura o modelo aislado de la base de datos.
  • Portable: Te permite transportar la estructura de tu ORM a cualquier DBMS.
  • Anidación de datos: En caso de que una tabla tenga una o varias relaciones con otras.

Cons

  • Lento: Si se compara el tiempo de respuesta entre un raw query y un query hecho por objetos, raw query es mucho mas rápido debido a que no existe una capa (mapping).
  • Complejidad: Algunas veces necesitaremos hacer queries complejos, por suerte Sequelize te permite ejecutar raw queries.

 

Que es Sequelize ?

Sequelize es un ORM basado en promesas para Node.js. Soporta PostgreSQL, MySQL, SQLite y MSSQL, y entrega características sólidas de transacciones, relaciones entre tablas, mecanismos de migraciones y carga de datos, y más.

 

Porque decidí utilizar Sequelize ?

Sequelize maneja sus objetos como promesas, algo que va de la mano con el event loop de Node.js.

 

Ahora les mostraré cómo crear y migrar tablas, cargar datos, y cómo consultar estos datos. Si quieren checar el código, pueden clonarlo desde aquí.

Requerimientos

  • Node.js 8.0+
  • MySQL 5.7+

Primero instalaremos globalmente el módulo sequelize-cli:

npm i -g sequelize-cli

 

Después crearemos una carpeta donde contenga nuestra aplicación, crearemos un archivo js e instalaremos sequelize y su dialecto (en este caso MySQL):

mkdir howto-sequelize && cd howto-sequelize
touch index.js
npm init -y
npm i -S sequelize mysql2

 

Ahora tenemos que iniciar el proyecto con sequelize-cli:

sequelize init

 

sequelize-cli nos creó una estructura base en la raíz de nuestro proyecto:

Si revisamos el archivo ./config/config.json, vemos que tenemos 3 opciones de conexión a una base de datos, modifiquemos la opción “development”:

"development": {
    "username": "root",
    "password": "tucontraseña",
    "database": "howto-sequelize",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "operatorsAliases": false,
    "dialectOptions": {
      "charset": "utf8mb4"
    },
    "logging": true,
    "benchmark": true
  }

 

Ahora revisemos el archivo ./models/index.js. Este archivo tiene como función crear una nueva instancia de Sequelize cada vez que sea llamado, y tiene como variable de entorno default a “development”, la cual utilizará la base de datos, host, usuario, contraseña y opciones que acabamos de agregar.

Creemos nuestra base de datos con el siguiente comando:

sequelize db:create

 

Muy bien! Ahora empecemos a crear nuestros modelos:

sequelize model:generate --name Usuario --attributes nombre:string,apellidoP:string,apellidoM:string,email:string
 
sequelize model:generate --name LenguajeP --attributes nombre:string
 
sequelize model:generate --name Usuario_LenguajeP --attributes
UsuarioId:integer,LenguajePId:integer

 

Después de crear nuestros modelos hay que hacer la relación entre usuarios y lenguajes.

./models/usuario.js

'use strict';
module.exports = (sequelize, DataTypes) => {
  var Usuario = sequelize.define('Usuario', {
    nombre: DataTypes.STRING,
    apellidoP: DataTypes.STRING,
    apellidoM: DataTypes.STRING,
    email: DataTypes.STRING
  }, {});
  Usuario.associate = function(models) {
    // associations can be defined here
    Usuario.belongsToMany(models.LenguajeP, {
      through: 'Usuario_LenguajeP',
      as: 'lenguajesProgramacion',
      foreignKey: 'UsuarioId',
    })
  };
  return Usuario;
};

Analícemos esto, vemos que nuestro modelo Usuario ejecuta una  función belongsToMany la cual apunta al modelo LenguajeP, en pocas palabras estamos indicando que un usuario puede pertenecer a varios lenguajes. En las opciones, through indica la tabla por la que tiene que cruzar para encontrar estas relaciones, as, es opcional, es el nombre de la propiedad o key que nos entregará estas relaciones,  foreignKey marca por que columna queremos que busque estas relaciones.

./models/lenguajep.js

'use strict';
module.exports = (sequelize, DataTypes) => {
  var LenguajeP = sequelize.define('LenguajeP', {
    nombre: DataTypes.STRING
  }, {});
  LenguajeP.associate = function (models) {
    // associations can be defined here
    LenguajeP.belongsToMany(models.Usuario, {
      through: 'Usuario_LenguajeP',
      as: 'usuarios',
      foreignKey: 'LenguajePId',
    })
  };
  return LenguajeP;
};

Hacemos lo mismo con los lenguajes (LenguajeP), pero ahora apuntando al modelo Usuario.

Recordemos un poco lo que platicamos arriba, ORM trabaja sobre una capa que es el mapping (mapeo), estas relaciones sólo se verán efectuadas en el proyecto, nos falta crear una migración que afecte a la base de datos. Existen ORM’s que revisan si has hecho cambios a tus modelos y crean nuevas migraciones a partir de estos cambios (Django ORM, peewee), en nuestro caso Sequelize no cuenta con eso, así que nosotros crearemos nuestras migraciones:

sequelize migration:generate --name relation-many-to-many

 

Esto nos generó un archivo nuevo con un esqueleto en nuestras migraciones, ahora tenemos que modificarlo:

'use strict';
 
module.exports = {
  up: (queryInterface, Sequelize) => {
   return [
    queryInterface.addConstraint('Usuario_LenguajeP', ['UsuarioId'], {
      type: 'FOREIGN KEY',
      name: 'FK_UsuarioLenguajeP_Usuario_1',
      references: {
        table: 'Usuario',
        field: 'id',
      },
      onDelete: 'no action',
      onUpdate: 'no action',
    }),
    queryInterface.addConstraint('Usuario_LenguajeP', ['LenguajePId'], {
      type: 'FOREIGN KEY',
      name: 'FK_UsuarioLenguajeP_LenguajeP_1',
      references: {
        table: 'LenguajeP',
        field: 'id',
      },
      onDelete: 'no action',
      onUpdate: 'no action',
    }),
  ]
  },
 
  down: (queryInterface, Sequelize) => {
   return [
    queryInterface.removeConstraint('Usuario_LenguajeP', 'FK_UsuarioLenguajeP_Usuario_1'),
    queryInterface.removeConstraint('Usuario_LenguajeP', 'FK_UsuarioLenguajeP_LenguajeP_1'),
  ]
  }
};

 

Este archivo exporta un objeto con 2 propiedades, up y down. La propiedad up está encargada de entregar una promesa que altere los datos (crear tablas, relaciones, campos, cambiar tipos, etc), y la propiedad down hace lo contrario, revierte los cambios que se hayan efectuado en up.

Muy bien, ahora toca mi parte favorita, hay que correr los scripts de migración con el siguiente comando:

sequelize db:migrate

 

BOOM! Pero qué pasó?!?! Si leemos con atención el error en la consola dice que no encuentra la tabla Usuario_LenguajeP en la base de datos, revisemos las tablas. Hay algo curioso, todas terminan con una “s”, esto es porque sequelize-cli maneja las tablas en plural por default, aún cuando en nuestras opciones tengamos freezeTableName: true, este caso lo pueden ver aquí.

Entonces solo nos falta cambiar el nombre de las tablas a plural en relation-many-to-many (Usuario_LenguajePs, Usuarios, LenguajePs).

Deshacemos las migraciones, volvemos a migrar y voilà:
sequelize db:migrate:undo:all
sequelize db:migrate

 

Las migraciones fueron exitosas! Ahora tenemos que poblar estas tablas, sequelize-cli utiliza las seeds (semillas). Vamos a crear 3 archivos seed:

sequelize seed:generate --name usuario
sequelize seed:generate --name lenguajep
sequelize seed:generate --name usuario_lenguajep

 

Estas semillas están ubicadas en la carpeta ./seeders, hay que poner los datos que deseamos cargar en la base de datos.

Semilla usuario:

'use strict';
 
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.bulkInsert('Usuarios', [
      {
        nombre: 'John',
        apellidoP: 'Q',
        apellidoM: 'Doe',
        email: 'johndoe@gmail.com',
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ], {});
  },
 
  down: (queryInterface, Sequelize) => {
   return queryInterface.bulkDelete('Usuarios', null, {});
  }
};

 

Semilla lenguajep:

'use strict';
 
module.exports = {
  up: (queryInterface, Sequelize) => {
   return queryInterface.bulkInsert('LenguajePs', [
    {
      nombre: 'Java',
      createdAt: new Date(),
      updatedAt: new Date(),
    },
    {
      nombre: 'JavaScript',
      createdAt: new Date(),
      updatedAt: new Date(),
    },
    {
      nombre: 'PHP',
      createdAt: new Date(),
      updatedAt: new Date(),
    },
    {
      nombre: 'Go',
      createdAt: new Date(),
      updatedAt: new Date(),
    },
    {
      nombre: 'C++',
      createdAt: new Date(),
      updatedAt: new Date(),
    },
  ], {});
  },
 
  down: (queryInterface, Sequelize) => {
   return queryInterface.bulkDelete('LenguajePs', null, {});
  }
};

 

Semilla usuario_lenguajep:

'use strict';
 
module.exports = {
  up: (queryInterface, Sequelize) => {
   return queryInterface.bulkInsert('Usuario_LenguajePs', [
    {
      UsuarioId: 1,
      LenguajePId: 2,
      createdAt: new Date(),
      updatedAt: new Date(),
    },
    {
      UsuarioId: 1,
      LenguajePId: 5,
      createdAt: new Date(),
      updatedAt: new Date(),
    },
  ], {});
  },
 
  down: (queryInterface, Sequelize) => {
   return queryInterface.bulkDelete('Usuario_LenguajePs', null, {});
  }
};

 

Ahora hay que cargar estos datos a la base de datos con el siguiente comando:

sequelize db:seed:all

 

Muy bién! Ya vimos cómo inicializar un proyecto con sequelize, crear modelos, asignar relaciones, crear migraciones, correr y deshacer migraciones, crear semillas y cargarlas a la base de datos, solo nos falta hacer queries. Vamos a nuestro index.js en raíz y escribimos la siguiente función:

const db = require('./models');
 
const main = async () => {
    try {
        const usuarios = await db.Usuario.findAll({
            include: [{
                model: db.LenguajeP,
                as: 'lenguajesProgramacion',
                attributes: { exclude: ['createdAt', 'updatedAt'] },
                through: { attributes: [] },
            }],
        });
        console.log(JSON.stringify(usuarios));
        process.exit();
    } catch (error) {
        console.log(error);
    }
};
 
main();

 

Lo guardamos y ejecutamos el programa (ya sea por consola o con la herramienta de debug de visual studio code) y deberíamos recibir un JSON con una lista de usuarios (en este ejemplo solo existe uno) con sus datos y lenguajes:

[
  {
    "id": 1,
    "nombre": "John",
    "apellidoP": "Q",
    "apellidoM": "Doe",
    "email": "johndoe@gmail.com",
    "createdAt": "2018-08-04T19:35:19.000Z",
    "updatedAt": "2018-08-04T19:35:19.000Z",
    "lenguajesProgramacion": [
      {
        "id": 2,
        "nombre": "JavaScript"
      },
      {
        "id": 5,
        "nombre": "C++"
      }
    ]
  }
]

Aprendimos sequelize-cli y lo básico de Sequelize, ahora podemos crear un API que ejecute acciones CRUD para que nuestros clientes la consuman!

2 Comments

  1. Existe alguna forma de crear los modelos en base a una schema database ya creada en mysql workbench? el cual me genera el script y la ejecuto para crear la base de datos y sus tablas
    o crear los modelos en base a una db ya creada? para no tener q usar las migraciones y cosas asi, segun me comentaron que no era muy apropiado utilizar las migraciones porque podria romperse y daria mas problemas

  2. Desconozco ese dato Samuel, pero encontre estos modulos:

    https://github.com/sequelize/sequelize-auto
    https://github.com/andyforever/sequelizer

    Puede que te sirvan para generar tus modelos a base de un Schema (Database First).

    Las migraciones ocasionan problemas, se “rompen”, cuando no tienes un buen seguimiento de tu Schema, sus tablas, relaciones, indexaciones, y/o campos que agreges, modifiques o elimines. Piensalo como un controlador de versiones, esa es la funcion principal de las migraciones, tener un historial del estado de tu base de datos. Te dejo esta lectura, esta algo extensa, pero toca puntos interesantes referente al tema de las migraciones (https://djrobstep.com/talks/your-migrations-are-bad-and-you-should-feel-bad).

  3. Donde Pedir Prestamos De Dinero Prestamos On Line Rapidos Donde Puedo Sacar Un Prestamo Rapido
    https://elprestamista.space/grand-prairie-tx/
    Creditos De Dinero Rapidos Creditos Personales Por Internet Creditos Pagina Web
    https://elprestamista.space/santa-clarita-ca/
    Prestamos Con Nomina Como Tener Un Prestamo Rapido Personal Cred Como Pedir Un Prestamo
    https://elprestamista.space/harlingen-tx/
    Credito 300 Creditos Personales Con Dni Creditos Rapidos Online Solicitud De Prestamo De Dinero Como Calcular El Interes De Un Prestamo Personal

  4. Porcentaje De Prestamos Personales Consulta De Credito Financieras Prestamos Rapidos Prestamos Confiables Y Rapidos
    https://creditosrapidosenusa.space/smyrna-ga/
    Solo Credito Requisitos Para Solicitar Un Prestamo Personal Solicitar Prestamo Online Prestamo Seguro
    https://creditosrapidosenusa.space/west-palm-beach-fl/
    Prestamos Urgentes En 24 Horas Solicitud De Prestamos Online Quiero Un Prestamo Personal
    https://creditosrapidosenusa.space/mansfield-tx/
    Microprestamos Rapidos Cooperativas De Credito Creditos Personales Inmediatos Solicitar Prestamo Por Internet Prestamos Rapidos Y Sencillos

  5. Creditos Personales Credito De Prestamos Individuales Prestamos 24 Horas Solicitar Prestamo Rapido Online
    https://prestamosenestadosunidos.space/beaumont-tx/
    A Que Interes Estan Los Prestamos Personales Tasa De Interes Prestamos Personales Prestadores De Dinero Prestamos En Linea
    https://prestamosenestadosunidos.space/houston-tx/
    Interes Mas Bajo En Prestamos Personales Comparador De Creditos Personales Credito 24 Horas Prestamos Para Negocios
    https://prestamosenestadosunidos.space/antioch-ca/
    Prestamistas De Dinero Rapido Prestadores De Dinero Que Es Un Prestamo Personal

  6. Prestamos Personales Por Internet 24 Horas Prestamos Personales Faciles Prestamos De Dinero Inmediato Necesito Un Prestamista De Dinero
    https://elprestamista.space/escondido-ca/
    Creditos Personales Prestamos Rapidito Prestamos Por Linea Inmediatos
    https://elprestamista.space/whitney-nv/
    Financieras De Prestamos Personales Como Conseguir Un Prestamo Personal Tasa De Interes Prestamos Personales Prestamos Confiables Y Rapidos Prestamos A Plazos
    https://elprestamista.space/mckinney-tx/
    Interes Credito Personal Prestamos Individuales Prestamos Seguros Por Internet Prestamos En Un Dia

  7. Prestamos Bajo Interes Solicitar Dinero Online Como Solicitar Un Credito Personal Prestamos Rapidos Y Seguros Prestamo Capital Privado
    https://prestamosenestadosunidos.space/laredo-tx/
    Busco Prestamista Prestamos A 30 Dias Donde Te Prestan Dinero Mas Rapido Como Conseguir Un Prestamo De Dinero
    https://prestamosenestadosunidos.space/atascocita-tx/
    Calcular Cuota Credito Personal Presta Mas Creditos Rapidos A Plazos
    https://prestamosenestadosunidos.space/newark-nj/
    Prestamo Familiar Como Pido El Prestamo En Personal Consulta De Credito Personal Prestamos 24h

  8. Prestamo De Dinero Facil Prestamo Personal Urgente Online Quiero Un Credito Rapido Microcredito Busco Prestamos Dinero
    https://prestamosenestadosunidos.space/yuma-az/
    Donde Puedo Solicitar Un Prestamo Rapido Creditos Rapidos Prestamos De Dinero Con Bajo Interes El Mejor Credito Personal Prestamos A 30 Dias
    https://prestamosenestadosunidos.space/las-vegas-nv/
    Solicitar Credito Online Mi Prestamo Personal Cotizador De Creditos Personales Simulador Prestamo Consumo
    https://prestamosenestadosunidos.space/vineland-nj/
    Prestamos A Pagar En Cuotas Microcredito Calculadora De Prestamos Donde Me Prestan Dinero Rapido Calculadora De Prestamos Personales

  9. Donde Puedo Conseguir Un Prestamo De Dinero Rapido Prestamos Economicos Personales Prestamos Personales Prestamistas Solicitar Prestamo Personal Rapido Tipo De Interes Prestamo Personal
    https://elprestamista.space/allen-tx/
    Prestamos Con Recibo De Sueldo Prestamos 24h Como Aplicar Para Un Prestamo Personal Prestamos De Credito
    https://elprestamista.space/carlsbad-nm/
    Quiero Sacar Un Prestamo Personal Creditos De Dinero Rapidos Prestamos Personales Con Nomina
    https://elprestamista.space/aurora-co/
    Solicitar Un Credito Rapido Por Internet Creditos Personales En El Acto Prestamo Privado Companias De Prestamos

  10. Prestamistas Urgentes Prestamos Chiquitos Solo Credito Prestamos Para Devolver A Plazos
    https://prestamosurgente.space/antioch-ca/
    Solicitud De Dinero Prestamos Faciles Por Internet Creditos En Linea Rapidos Como Pedir Prestamo A Personal
    https://prestamosurgente.space/tucson-az/
    Financieras Creditos Rapidos Prestamos Al Consumo Credito Facil Rapido Online
    https://prestamosurgente.space/temecula-ca/
    Financiamiento A Corto Plazo Financieras De Prestamos Personales Creditos De Dinero Prestamos Con Tarjeta De Credito Online

  11. Prestamos De Dinero Rapido Prestamos En Linea En 24 Horas Prestamos Sencillos Prestar Dinero Por Internet Donde Sacar Prestamos Personales
    https://prestamosurgente.space/sayreville-nj/
    Busco Personas Que Prestan Dinero Rapido Mini Prestamos A Plazos Lugares De Prestamos Prestamos De Dinero Con Dni Buscar Prestamos Personales Urgentes
    https://prestamosurgente.space/east-brunswick-nj/
    Prestar Dinero Con Intereses Como Consigo Un Prestamo Rapido Credito 300 Euros Prestamista
    https://prestamosurgente.space/west-new-york-nj/
    Prestamos Grandes Y Rapidos Creditos Para Empresas Prestamos Personales Online Urgentes

  12. Prestamos Credito Etc Donde Sacar Prestamos Personales Interes Prestamo Personal
    https://elprestamista.space/gainesville-fl/
    Pedir Dinero Urgente Mini Prestamos Prestamos Y Creditos Online
    https://elprestamista.space/buckeye-az/
    Prestamos Para Devolver A Plazos Empresas De Prestamos Personales Consultar Credito Personal Todo Facil Prestamos Donde Hacen Prestamos De Dinero
    https://elprestamista.space/irving-tx/
    Mejores Prestamos Credito Personal Que Cuenta Es Prestamo Personal 2000 Euros

  13. Pedir Prestamo Donde Pedir Un Prestamo Personal Minicreditos
    https://elprestamista.space/silver-city-nm/
    Para Pedir Un Credito Rapido Personas Que Dan Prestamos De Dinero Dinero Prestamos Personales
    https://elprestamista.space/elko-nv/
    Credito Privado Casa De Prestamos De Dinero Credito De Consumo Creditos Personales Urgentes Intereses De Prestamos
    https://elprestamista.space/irvington-nj/
    Lugares De Prestamos Pedir Prestamo Online Rapido Prestamos De Dinero Con Dni Microcredito

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *