No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

3D
23H
41M
23S

Consideraciones al hacer migraciones

26/27
Recursos

Aportes 19

Preguntas 13

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Me carga el payaso… tengo más de 20 migraciones porque me puse a jugar con las tablas y las columnas en clases anteriores. Me tocó hacer una faena épica.

Mi archivo único de migraciones es algo como esto:

'use strict';
const { DataTypes, Sequelize } = require('sequelize');
const { USER_TABLE, UserSchema } = require('../models/userModel')
const { CUSTOMER_TABLE, CustomerSchema } = require('../models/customerModel')
const { CATEGORY_TABLE, CategorySchema } = require('../models/categoryModel')
const { PRODUCT_TABLE, ProductSchema } = require('../models/productModel')
const { ORDER_TABLE } = require('../models/orderModel')
const { OrderProductSchema, ORDER_PRODUCT_TABLE } = require('../models/order-productModel')


module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
          },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
        },
    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);

  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE)
    await queryInterface.dropTable(CUSTOMER_TABLE)
    await queryInterface.dropTable(CATEGORY_TABLE)
    await queryInterface.dropTable(PRODUCT_TABLE)
    await queryInterface.dropTable(ORDER_TABLE)
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE)
  }
};

Grande Nicolás! Te la comiste con este curso. Mil gracias, aprendí un montón!

este curso está 10/10 ☄

En mi opinión (para este caso) es mucho más sencillo borrar todas las migraciones y crear una sola en limpio con su respectivo código.

Con esto no habría ningún inconveniente al subirlo a producción. Claro, habría otras complicaciones más adelante en Development, pero si no tienen problema, pueden borrar y crear nuevamente las tablas de la base de datos.

Las migraciones son muy delicadas, es por ello que se deben tener en cuenta los esquemas. A nivel producción no se recomienda crear migraciones paso a paso, más bien, crear una sola migración que ya tenga todas las relaciones y configuración por defecto.

Una buena practica es no repetir el código, pero en este caso se va necesitar repetir el esquema cuando se creen las tablas.

Cada vez que agregamos un atributo o algo parecido, lo mejor es no basarse en el esquema porque es algo genérico y no está versionado, sirve para el modelo, pero no es buena practica que sea la base de las migraciones porque lo configuramos en cualquier momento y puede dañar la configuración.

Los archivos que se cambiaron fueron los siguientes:

Migración create-user.js:

'use strict';

const { USER_TABLE } = require('../models/user.model');
const { DataTypes, Sequelize } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      email: {
        allowNull: false,
        type: DataTypes.STRING,
        unique: true,
      },
      password: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE);
  },
};

Migración add-role.js:

'use strict';

const { USER_TABLE } = require('../models/user.model');
const { DataTypes } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.addColumn(USER_TABLE, 'role', {
      allowNull: false,
      type: DataTypes.STRING,
      defaultValue: 'customer',
    });
  },

  down: async (queryInterface) => {
    await queryInterface.removeColumn(USER_TABLE, 'role');
  },
};

Migración create-customers.js:

'use strict';

const { CUSTOMER_TABLE } = require('../models/customer.model');
const { USER_TABLE } = require('../models/user.model');
const { DataTypes, Sequelize } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(CUSTOMER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      lastName: {
        allowNull: false,
        type: DataTypes.STRING,
        field: 'last_name',
      },
      phone: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
      },
      userId: {
        field: 'user_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: USER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(CUSTOMER_TABLE);
  },
};

Migración change-user-id.js:

'use strict';

const { CUSTOMER_TABLE } = require('../models/customer.model');
const { DataTypes } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.changeColumn(CUSTOMER_TABLE, 'user_id', {
      field: 'user_id',
      allowNull: false,
      type: DataTypes.INTEGER,
      unique: true,
    });
  },

  down: async (queryInterface) => {
    // await queryInterface.dropTable(CUSTOMER_TABLE);
  },
};

Migración order.js:

'use strict';

const { ORDER_TABLE } = require('../models/order.model');
const { CUSTOMER_TABLE } = require('../models/customer.model');
const { DataTypes, Sequelize } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(ORDER_TABLE);
  },
};

Error ''VIRTUAL'

Con la configuración de ssl del video no me ha funcionado. Parece que para un pool si sirve poner:

dialectOptions: {
            ssl: {
                rejectUnauthorized: false
            }
        }

Pero me seguía dando un error de pg_hba.conf y ssl en off. He tenido que añadir:

dialectOptions: {
            ssl: {
                require: true,
                rejectUnauthorized: false
            }
        }

Me seguía falando y encontré un aporte que indicaba que había que definir el modo:

heroku config:set PGSSLMODE=no-verify

Con este curso me he dado cuenta de que el Frontend es definitivamente más fácil que el Backend 😅

La funcionalidad del rango de precios de productos quedó mal 😦

Uff ya quiero trabajar en otro proyecto igual.

No me quedaré con la duda. Lo que entiendo es que las migraciones en la pratica nos servirán para que se creen todas las tablas en nuestra base de datos nueva. ¿Estoy bien en eso?

Si desde el inicio ya tengo claro como quiero que sea mi base de datos y no tengo que hacer fixes porque desde el inicio se lo que se iba a presentar. ¿Podría simplemente eliminar la creación de crear el rol puesto que si ya estaba en el esquema en la migración anterior se crearía?

No se si me hago entender, me gustaría salir de esa duda.

Heroku no tienes planes gratuito a la fecha! hice mi deploy en render el repositorio de github

Una opción de deploy es Railway yo lo use así:

  • Primero deben de configuarar las migraciones en mi caso borre todas las migraciones y solo cree una:
'use strict';
const { DataTypes, Sequelize } = require('sequelize');
const { USER_TABLE, UserSchema } = require('./../models/user.model')
const { CUSTOMER_TABLE, CustomerSchema } = require('./../models/customer.model')
const { CATEGORY_TABLE, CategorySchema } = require('./../models/category.model')
const { PRODUCT_TABLE, ProductSchema } = require('./../models/product.model')
const { ORDER_TABLE } = require('./../models/order.model')
const { OrderProductSchema, ORDER_PRODUCT_TABLE } = require('./../models/order-product')



module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
          },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
        },

    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);

  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE)
    await queryInterface.dropTable(CUSTOMER_TABLE)
    await queryInterface.dropTable(PRODUCT_TABLE)
    await queryInterface.dropTable(CATEGORY_TABLE)
    await queryInterface.dropTable(ORDER_TABLE)
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE)
  }
};
  • Se crean un archivo Dockerfile en la raíz del proyecto, ya que Railway solo puede ejecutar este archivo de docker:
    Dockerfile
FROM node:16 AS build

RUN mkdir -p /usr/src/app

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm ci

COPY . .

RUN npm run migrations:run

EXPOSE 8080

CMD ["npm", "start"]
  • Primero creamos una DB en Railway y copiamos la URL de conexión en su apartado de conexión
  • En esta parte debemos ir al archivo .env y pegar la url de conexión:
    .env
DATABASE_URL='urlderailway'
  • En nuestro .gitignore eliminamos el archivo .env y lo subimos a un repo de github. Y creamos el proyecto para nuestra aplicaión en Railway

  • Creamos el servicio y debería correr la migración sin problemas.

  • NOTA: esta opción nos dejará hacer el deploy pero es una mala practica ya que estaremos dejando expuesta nuestra varible de entorno al subir el archivo .env . Por lo que es solo una buena opción para aprender pero para producción no.
    No se si se podrá ya que al momento de crear el proyecto del servicio railway nos permite pasarle variables de entorno pero por alguna razón (en mi caso) no detectaba o no le llegaba la variable de entorno, es por eso, por el momento que lo solucione así, si alguien pudo subirlo en Railway espero su feedback. Gracias.

DE ESTA MANERA SUBIMOS A Railway:
De esta manera pude yo subirlo a Railway sin borrar el .env del .gitignore Configure las migraciones a una sola:

'use strict';

const { DataTypes, Sequelize } = require('sequelize');
const { USER_TABLE } = require('./../models/user.model');
const { CUSTOMER_TABLE } = require('./../models/customer.model');
const { CATEGORY_TABLE } = require('./../models/category.model');
const { PRODUCT_TABLE } = require('./../models/product.model');
const { ORDER_TABLE } = require('./../models/order.model');
const { ORDER_PRODUCT_TABLE } = require('./../models/order-product.model');

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      email: {
        allowNull: false,
        type: DataTypes.STRING,
        unique: true,
      },
      password: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
    });
    await queryInterface.createTable(CUSTOMER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      firstName: {
        allowNull: false,
        type: DataTypes.STRING,
        field: 'first_name',
      },
      lastName: {
        allowNull: false,
        type: DataTypes.STRING,
        field: 'last_name',
      },
      phone: {
        allowNull: true,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
      userId: {
        field: 'user_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: USER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
    await queryInterface.createTable(CATEGORY_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        unique: true,
        type: DataTypes.STRING,
      },
      image: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        defaultValue: Sequelize.NOW,
        field: 'create_at',
        type: DataTypes.DATE,
      },
    });
    await queryInterface.createTable(PRODUCT_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      image: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      description: {
        allowNull: false,
        type: DataTypes.TEXT,
      },
      price: {
        allowNull: false,
        type: DataTypes.INTEGER,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
      categoryId: {
        field: 'category_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CATEGORY_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      createdAt: {
        allowNull: false,
        defaultValue: Sequelize.NOW,
        field: 'create_at',
        type: DataTypes.DATE,
      },
    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
      amount: {
        allowNull: false,
        type: DataTypes.INTEGER,
      },
      orderId: {
        field: 'order_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: ORDER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      productId: {
        field: 'product_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: PRODUCT_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE);
    await queryInterface.dropTable(CUSTOMER_TABLE);
    await queryInterface.dropTable(CATEGORY_TABLE);
    await queryInterface.dropTable(PRODUCT_TABLE);
    await queryInterface.dropTable(ORDER_TABLE);
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE);
  },
};

Se crean un archivo **Dockerfile ** en la raíz del proyecto:

FROM node:16 AS build

RUN mkdir -p /usr/src/app

WORKDIR /usr/src/app

ARG NODE_ENV
ARG DATABASE_URL

ENV NODE_ENV=$NODE_ENV
ENV DATABASE_URL=$DATABASE_URL

COPY package*.json ./
RUN npm ci

COPY . .

RUN npm run migrations:run

EXPOSE 8080

CMD ["npm", "start"]

Primero creamos una DB en Railway y copiamos la URL de conexión en su apartado de conexión , le dan a New , luego Database - Postgres y le tiene que quedar como la imagen, Las variables de entorno se configuran automaticamente:

Qué pasaria si, borro la migración de add-role y dejo que el create-user cree el modelo como esta definido ya con el role?

Tuve un error al crear la migración ya que me aparecia un error con el autoIncrement en la primera tabla.
El error era el siguente:

ERROR: syntax error at or near "SERIAL"

Se pudo corregir dandole el valor de autoIncrement: false, y se agrega la propiedad de defaultValue: UUIDV4: Quedando de la siguente forma

id: {
        allowNull: false,
        autoIncrement: false,
        primaryKey: true,
        type: DataTypes.STRING,
        defaultValue: UUIDV4,
      } 

Tomen en cuenta cambiar todas los id de este tipo, también con la llaves foráneas.

Esta clase es como un backlog y documento de lecciones aprendidas pero en formato mp4

El contenido del archivo de migración única:

'use strict';


const { UserSchema, USER_TABLE } = require('./../models/userModel');
const { CustomerSchema, CUSTOMER_TABLE } = require('./../models/customerModel');
const { ProductSchema, PRODUCT_TABLE } = require('./../models/productModel');
const { CategorySchema, CATEGORY_TABLE } = require('./../models/categoryModel');
const { OrderSchema, ORDER_TABLE } = require('./../models/orderModel');
const { OrderProductSchema, ORDER_PRODUCT_TABLE } = require('./../models/order-productModel');

module.exports = {
  async up (queryInterface) {
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(ORDER_TABLE, OrderSchema);
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);
  },

  async down (queryInterface) {
    await queryInterface.dropTable(USER_TABLE);
    await queryInterface.dropTable(CUSTOMER_TABLE);
    await queryInterface.dropTable(PRODUCT_TABLE);
    await queryInterface.dropTable(CATEGORY_TABLE);
    await queryInterface.dropTable(ORDER_TABLE);
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE);
  }
};