Running Sql migrations throws syntax error - mysql

I have created my first migration using sequelize-cli, now when I enter npx sequelize-cli db:migrate to run migration and create table in DB, I get error
I look into documentation could not find how and what should go into migration file.
Error
ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NUMBER, `otp` INTEGER, `otp_expiration_date` DATETIME, `createdAt` DATETIME NOT ' at line 1
My migration File
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
phone_number: {
type: Sequelize.NUMBER
},
otp: {
type: Sequelize.INTEGER(4)
},
otp_expiration_date: {
type: Sequelize.DATE
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
My User Model:
const moment = require('moment');
'use strict';
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
User.init({
name: {
type: DataTypes.STRING,
allowNull: false
},
phone_number: {
type: DataTypes.NUMBER
},
otp: {
type: DataTypes.INTEGER(4)
},
otp_expiration_date: {
type: DataTypes.DATE,
set(value) {
// convert regular Date to moment Date
value = moment(value).add(5, 'minutes');
this.setDataValue('otp_expiration_date', value);
}
},
is_otp_expired: {
type: DataTypes.VIRTUAL,
get() {
// otp_expiration_date < current date
return this.getDataValue(otp_expiration_date).isAfter(moment()) ? true : false
}
}
}, {
sequelize,
modelName: 'User',
});
return User;
};
I have tried
changing datatypes
moving getters into migration
removing createdAt

[SOLVED]
Problem was with DataType of phone_number , there is no Sequelize.NUMBER type per Sequelize docs
I confused Sequelize DataTypes with MySql DataTypes
phone_number: {
type: DataTypes.NUMBER <---- bug
}
Solution
phone_number: {
type: DataTypes.INTEGER <---- solution
}

Related

Sequelize UUID with mySQL: Unable to set default value

I've been trying for awhile to set id (primary key) for my Users table as UUID. However, I keep getting this error: Field 'id' doesn't have a default value, when I attempt to seed it.
This is what I have so far in my Users model:
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Users extends Model {};
Users.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
primaryKey: true
},
user_name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: 'Please add a name',
},
},
},
{
sequelize,
modelName: 'Users',
});
return Users;
Likewise, this is what I have in my Users migration file:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Admins', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false,
primaryKey: true
},
user_name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
I'm pretty new to Sequelize, so would love some guidance on what's gone wrong!
By adding defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), to your createdAt and updatedAt in your migration file defaults the value to the current timestamp.
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
},
So, I think I realised the issue:
For some reason, seeding it by using a seeder file would not auto-generate the fields that I thought would be auto-generated, so I had to put them in manually.
'use strict';
const { v4: uuidv4 } = require('uuid');
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.bulkInsert('Users', [{
id: uuidv4(),
user_name: 'John Doe',
"createdAt": new Date(),
"updatedAt": new Date()
}], {});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.bulkDelete('Users', null, {});
}
};
Initially, I had been trying to seed (running the command npx sequelize-cli db:seed:all) without the id: uuidv4() and the new Date(), which was why it didn't work.

Sequelize using postgres dialect add double column

I want to create a relationship between parents table and children table and this is how i doing it
// 20210226075430-create-child.js (children migration file)
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("Children", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
},
age: {
type: Sequelize.STRING,
},
parentId: {
type: Sequelize.INTEGER,
allowNull: false,
onDelete: "CASCADE",
references: {
model: "Parents",
key: "id",
},
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Children");
},
};
// parent migration file
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("Parents", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
},
age: {
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Parents");
},
};
// child.js / children model
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Child extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Child.belongsTo(models.Parent, { foreignKey: "parentId" });
}
}
Child.init(
{
name: DataTypes.STRING,
age: DataTypes.STRING,
parentId: DataTypes.INTEGER,
},
{
sequelize,
modelName: "Child",
}
);
return Child;
};
// parent model
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Parent extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Parent.hasMany(models.Child);
}
}
Parent.init(
{
name: DataTypes.STRING,
age: DataTypes.STRING,
},
{
sequelize,
modelName: "Parent",
}
);
return Parent;
};
this code perfectly fine in mysql but when i change dialect to postgres i got error like this :
Executing (default): SELECT "id", "name", "age", "parentId", "createdAt", "updatedAt", "ParentId" FROM "Children" AS "Child";
(node:7496) UnhandledPromiseRejectionWarning: SequelizeDatabaseError: column "ParentId" does not exist
at Query.formatError (D:\work\www\express-starter\node_modules\sequelize\lib\dialects\postgres\query.js:386:16)
at Query.run (D:\work\www\express-starter\node_modules\sequelize\lib\dialects\postgres\query.js:87:18)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:7496) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:7496) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I have already tried to add foreign key in child.js (model) but i still got this error.
where i doing it wrong?
You didn't indicate parentId as a foreignKey option in the association from Parent to Children that's why Sequelize automatically generates its name like ParentId.
Just indicate the same option and value as in belongsTo like this:
Parent.hasMany(models.Child, { foreignKey: "parentId" });

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.
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!

Sequelize primaryKey as UUID in MySql database - not working

I'm facing the problem with implementation model in Sequalize with primary key as uuid. I follow step by step all instruction, but still I cannot solve it.
This is how my model looks like:
'use strict';
module.exports = (sequelize, DataTypes) => {
var Todo = sequelize.define('Todo', {
title: DataTypes.STRING,
description: DataTypes.STRING,
test: DataTypes.UUID
}, {});
Todo.associate = function(models) {
// associations can be defined here
};
return Todo;
};
And migration file:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Todos', {
id: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV1
},
title: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
test: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Todos');
}
};
After calling post method to create element, I habe got an empty value in ID column, and NULL in test column...
The Datebase is MySql.
Is there any other way to do it?
1) Setting Custom Keys in Sequelize
I'm facing the problem with implementation model in Sequalize with
primary key as uuid.
By default, if your model does not contain a key with primaryKey: true, then sequelize assumes a PK of type INTEGER named id.
In your case, it seems like you wish to make your own custom PK.
Use the following in your model:
var Todo = sequelize.define('Todo', {
id: {
primaryKey: true,
type: DataTypes.UUID
}
// rest of properties
});
2) Validations
After calling post method to create element, I habe got an empty value in ID column, and NULL in test column...
Without much information regarding not only your query, but how you seeded the database, it's hard to answer specifically.
However, it doesn't surprise me that test column was null, because you have not listed any validations. Thus if you seed/create rows that do not set a test value, it will be null.
To create validations do the following
model:
var Todo = sequelize.define('Todo', {
// rest of properties
test: {
allowNull: false
type: DataTypes.UUID,
validate: {
notNull: true
}
}
});
migration:
queryInterface.createTable('Todos', {
// rest of properties
test: {
allowNull: false,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
}
});
3) Keeping Models and Migrations Synchronized
(see: https://stackoverflow.com/a/49796801/8954866) In sequelize, models and migrations are not automatically in-sync with one another, except when initially generated using sequelize-cli model:generate. I'm unsure if you ran your migrations or if you were running your query in a unit test against the model. But you have to make sure they are synchronized. A primary example is that in the above case, your migration says id is of type UUID, but your model will think it's of type INTEGER.
References
Setting Custom Primary Keys
Validations and allowNull
Make sure your package.json has updated uuid module, this was the problem in my case.
accessSecret: {
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false,
unique: true,
},
This works fine,
just make sure package.json has
"uuid": "^8.2.0",
Looks like this issue is the same for both MySQL and Postgres: Sequelize needs to use the defaultValue setting of the model to point to its uuid dependency.
For example, for an entity called Product, create your migration like this:
"use strict";
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("Products", {
id: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4,
},
productName: {
type: Sequelize.STRING,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Products");
},
};
Then, create your model like this:
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Product extends Model {
static associate(models) {
// associations
}
}
Product.init(
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
productName: DataTypes.STRING,
},
{
sequelize,
modelName: "Product",
}
);
return Product;
};
Note that in your model, you're defining defaultValue: DataTypes.UUIDV4.
When you create a new instance of Product, you only need the name field. The UUID is auto-generated by the Sequelize model.
const {
Product
} = require("./models");
const createProduct = async (name) => {
const newProductData = { productName: "First product" };
const creationResult = await Product.create(newProductData);
};
The id field in the model will trace the defaultValue setting back to the v4() function of the uuid package that Sequelize relies on to auto-generate the UUID as part of the model operation, not as part of native Postgres.

How to add new attributes to schema after the migration?

currently I am working on a node.js project, and I found a problem while I was building the schema, usually, I use command line provided by http://docs.sequelizejs.com/manual/tutorial/migrations.html to define my schema, which is $ node_modules/.bin/sequelize model:generate --name User --attributes firstName:string,lastName:string,email:string , and after the $ node_modules/.bin/sequelize db:migrate, I can write these attributes into database. However, I am wondering how to add new attribute to schema after the migration, I searched and found this https://github.com/sequelize/cli/issues/133 is discussing this problem, but after I tried the solution and run $ node_modules/.bin/sequelize db:migrate again, it did not write the new attributes to the original schema, I don't understand where's the problem, below is my code, I am trying to add two attributes 'address'& 'height' into the user schema, can you guys give me some advice? Thank you!
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
let migration = [];
migrations.push(queryInterface.addColumn(
'address',
'height',
{
type: Sequelize.STRING,
}
));
return Promise.all(migrations);
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
firstName: {
type: Sequelize.STRING,
},
lastName: {
type: Sequelize.STRING,
},
email: {
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
this issue has been solved, you can use below link How to Add, Delete new Columns in Sequelize CLI to solve that issue, also you can check the query part of sequelize to see the documents http://docs.sequelizejs.com/class/lib/query-interface.js~QueryInterface.html, The real answer should be
module.exports = {
up: function (queryInterface, Sequelize) {
return [
queryInterface.addColumn(
'users',
'height',
{
type: Sequelize.STRING,
}
),
queryInterface.addColumn(
'users',
'address',
{
type: Sequelize.STRING,
}
)
];
},
down: function (queryInterface, Sequelize) {
return [
queryInterface.removeColumn('users', 'height'),
queryInterface.removeColumn('users', 'address')
];
}
};
and then you can type
sequelize migration:create --name add-height-and-address-to-user
sequelize db:migrate
to migrate new attributes into your model.