khukho
khukho

Reputation: 476

Sequelize - Model and Migrations

[Resolved !]

I've researched a lot on the Internet and found some similar question with mine. But, I can't find the proper way to fix my problem. Please help.

I'm using Sequelize v6. I've some troubles in using models and migrations.

What I've done:

I generated role model using sequelize cli. And it gives me below code in models/role.js.

"use strict";
const { Model } = require("sequelize");

module.exports = (sequelize, DataTypes) => {
  class Role extends Model {
    static associate(models) {
      // define association here
    }
  }

  Role.init(
    {
      name: DataTypes.STRING,
      description: DataTypes.STRING,   },
    {
      sequelize,
      modelName: "roles",
    }
  );

  return Role;
};

And I also got migration file for that model in migrations/timestamp-create-role.js.

"use strict";

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable(
      "roles", // <- I've changed this from Roles to roles because I want the MySQL convention.
      {
        roleId: {
          allowNull: false,
          autoIncrement: true,
          field: "role_id",
          primaryKey: true,
          type: Sequelize.INTEGER,
        },
        name: {
          allowNull: false,
          type: Sequelize.STRING(30),
        },
        description: {
          type: Sequelize.STRING(50),
        },
        createdAt: {
          allowNull: false,
          field: "created_at",
          type: Sequelize.DATE,
        },
        updatedAt: {
          allowNull: false,
          field: "updated_at",
          type: Sequelize.DATE,
        },
      },
      {
        underscored: true,
      }
    );
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable("roles");
  },
};

What I got in MySQL

By running the above migration, I got roles table with following columns:

role_id, name, created_at, updated_at which is fine. That is what I want in MySQL database.

Reading roles

/controllers/roles.js

const Role = require("../models").roles;
const catchAsync = require("../middlewares/catchAsync");

exports.findAll = catchAsync(async (req, res) => {
  const roles = await Role.findAll();

  return res.status(200).json({
    status: "success",
    data: {
      roles,
    },
  });
});

What I'm not OK

With the above implementation, I got this error on the console.

Executing (default): SELECT `id`, `name`, `description`, `createdAt`, `updatedAt` FROM `roles` AS `roles`;
SequelizeDatabaseError: Unknown column 'id' in 'field list'
    at Query.formatError (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/dialects/mysql/query.js:239:16)
    at Query.run (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/dialects/mysql/query.js:54:18)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async /home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/sequelize.js:619:16
    at async MySQLQueryInterface.select (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/dialects/abstract/query-interface.js:938:12)
    at async Function.findAll (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/model.js:1741:21)
    at async /home/hello-world/Thesis/SMS-API/src/controllers/roles.js:5:17
    at async /home/hello-world/Thesis/SMS-API/src/middlewares/catchAsync.js:4:7

Another Attempt

I've modified models/role.js like below.

Role.init(
    {
      role_id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: DataTypes.STRING,
      description: DataTypes.STRING,
      created_at: DataTypes.DATE,
      updated_at: DataTypes.DATE,
    }

Next try, Next Error

With the above modification, I got new error.

Executing (default): SELECT `role_id`, `name`, `description`, `created_at`, `updated_at`, `createdAt`, `updatedAt` FROM `roles` AS `roles`;
SequelizeDatabaseError: Unknown column 'createdAt' in 'field list'
    at Query.formatError (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/dialects/mysql/query.js:239:16)
    at Query.run (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/dialects/mysql/query.js:54:18)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async /home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/sequelize.js:619:16
    at async MySQLQueryInterface.select (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/dialects/abstract/query-interface.js:938:12)
    at async Function.findAll (/home/hello-world/Thesis/SMS-API/node_modules/sequelize/lib/model.js:1741:21)
    at async /home/hello-world/Thesis/SMS-API/src/controllers/roles.js:5:17
    at async /home/hello-world/Thesis/SMS-API/src/middlewares/catchAsync.js:4:7

HELP

I've tried so many ways which cannot fix my problem yet. So, please help. I want MySQL naming convention in MySQL level which is underscore and JavaScript naming convention in Code level.

Thank you so much.

Upvotes: 2

Views: 3699

Answers (1)

khukho
khukho

Reputation: 476

I've fixed my problem. I can now use each naming convention for their specific world: under_score for MySQL and camelCase for JavaScript.

So here I've written my solution if someone come across the same issue in the future.

I'm using sequelize-cli for creating migrations, models and seeders. You can check it here.

Migration

Migration is only responsible for creating/altering/deleting the tables and columns. It access with only the database.

migrations/timestamp-create-role-table.js

"use strict";

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable("roles", {
      roleId: {
        allowNull: false,
        autoIncrement: true,
        field: "role_id",
        primaryKey: true,
        type: Sequelize.INTEGER,
      },
      name: {
        allowNull: false,
        type: Sequelize.STRING(30),
      },
      description: {
        type: Sequelize.STRING(50),
      },
      createdAt: {
        allowNull: false,
        field: "created_at",
        type: Sequelize.DATE,
      },
      updatedAt: {
        allowNull: false,
        field: "updated_at",
        type: Sequelize.DATE,
      },
    });
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable("roles");
  },
};

By running the above migration script, I got the roles table with following columns in my physical database. role_id, name, description, created_at, updated_at

Seeder

Seeder will populate your table with initial test data. I've filled my roles table like below.

seeders/timestamp-roles-table-seeder.js

"use strict";

const roles = [
  "official",
  "office",
  "admin"
].map((role) => {
  return {
    name: role,
    created_at: new Date(),
    updated_at: new Date(),
  };
});

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.bulkInsert("roles", roles);
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.bulkDelete("roles", null, {});
  },
};

Model

After I've created table and filled up with initial data, I need to create Role model in order to use as bridge between MySQL and JavaScript.

models/role.js

"use strict";
const { Model } = require("sequelize");

module.exports = (sequelize, DataTypes) => {
  class Role extends Model {
    static associate(models) {
      // define association here
    }
  }

  Role.init(
    {
      roleId: {
        allowNull: false,
        autoIncrement: true,
        field: "role_id", // SELECT role_id AS id
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: DataTypes.STRING,
      description: DataTypes.STRING,
      createdAt: {
        type: DataTypes.DATE,
        field: "created_at",
      },
      updatedAt: {
        type: DataTypes.DATE,
        field: "updated_at",
      },
    },
    {
      sequelize,
      tableName: "roles",
      modelName: "Role",
    }
  );

  return Role;
};

Getting roles from JavaScript

controllers/roles.js

const models = require("../models");
const catchAsync = require("../middlewares/catchAsync");

exports.findAll = catchAsync(async (req, res) => {
  const roles = await models.Role.findAll();

  /*
  you can access 
    - role_id from db with roleId in JS
    - created_at from db with createdAt in JS
  */

  return res.status(200).json({
    status: "success",
    data: {
      roles,
    },
  });
});

Hope it could help!

Upvotes: 2

Related Questions