Custom validation error using Sequelize.js - javascript

Is possible to customize the error from the
Sequelize.ValidationError
Model:
var PaymentType = sequelize.define('payment_type' , {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
code: {
type: DataTypes.STRING,
allowNull: false,
validate:{
notEmpty: true
},
field: 'code'
},
name: {
type: DataTypes.STRING,
allowNull: false,
validate:{
notEmpty: true
},
field: 'name'
}
}, {
timestamps: true,
paranoid: false,
underscored: true,
freezeTableName: true,
tableName: 'payment_types'
});
My controller:
update(req, res) {
paymentType
.update(req.body, {
where: {
id: req.params.id
}
})
.then( updatedRecords => {
res.status(200).json(updatedRecords);
})
.catch(Sequelize.ValidationError, error => {
res.status(400).json(error);
})
.catch( error => {
res.status(500).json(error);
});
},
The errors I get, are this way:
{
"name": "SequelizeValidationError",
"message": "Validation error: Validation notEmpty failed",
"errors": [
{
"message": "Validation notEmpty failed",
"type": "Validation error",
"path": "name",
"value": {},
"__raw": {}
}
]
}
I want to pass the errors like this(only path and message):
{
"name":"The field cannot be empty",
"other_field":"custom error message"
}
I don't know if I can specify a custom message in the model or I have to create a function to build the errors messages.
If I have to build a function, how can I extract the path and customize the message?
Thanks in Advance.

You can catch Sequelize's ValidationError and loop through its ValidationErrorItem's and write a custom message depending on the error type (determined by ValidationErrorItem.validatorKey). Here is a code snippet to get started.
try {
// sequelize custom logic here
} catch(e) {
const messages = {};
if (e instanceof ValidationError) {
e.errors.forEach((error) => {
let message;
switch (error.validatorKey) {
case 'isEmail':
message = 'Please enter a valid email';
break;
case 'isDate':
message = 'Please enter a valid date';
break;
case 'len':
if (error.validatorArgs[0] === error.validatorArgs[1]) {
message = 'Use ' + error.validatorArgs[0] + ' characters';
} else {
message = 'Use between ' + error.validatorArgs[0] + ' and ' + error.validatorArgs[1] + ' characters';
}
break;
case 'min':
message = 'Use a number greater or equal to ' + error.validatorArgs[0];
break;
case 'max':
message = 'Use a number less or equal to ' + error.validatorArgs[0];
break;
case 'isInt':
message = 'Please use an integer number';
break;
case 'is_null':
message = 'Please complete this field';
break;
case 'not_unique':
message = error.value + ' is taken. Please choose another one';
error.path = error.path.replace("_UNIQUE", "");
}
messages[error.path] = message;
});
}
}

You can specify a custom message for sequelize Validation, Your code will look something like this
`var PaymentType = sequelize.define('payment_type' , {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
code: {
type: DataTypes.STRING,
allowNull: false,
validate:{
notEmpty: {
args: true,
msg: “code cannot be empty"
}
},
field: 'code'
},
name: {
type: DataTypes.STRING,
allowNull: false,
validate:{
notEmpty: {
args: true,
msg: “code cannot be empty"
}
},
field: 'name'
}
I specified the validation rule as an object, in the above case, notEmpty should be true so I refactored it to an object setting the args property to true i.e notEmpty should be true, and the second property is msg which contains our custom message
notEmpty: {
args: true,
msg: “code cannot be empty"
}

Do you try to change Model like this:
var PaymentType = sequelize.define('payment_type' , {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
code: {
type: DataTypes.STRING,
allowNull: false,
validate:{
notEmpty: {
msg: "The field cannot be empty"
}
},
field: 'code'
},
name: {
type: DataTypes.STRING,
allowNull: false,
validate:{
notEmpty: {
msg: "The field cannot be empty"
}
},
field: 'name'
}
}, {
timestamps: true,
paranoid: false,
underscored: true,
freezeTableName: true,
tableName: 'payment_types'
});
Reference this post

Related

edit column/attributes in sequelize migration

Hi I'm new to migrations in sequelize, and I'm not sure how to add field/property for attributes. So my scenario is that I have two attributes sku & barcode, but I forgot to add unique: true. Now I need to edit the table, I have tried with addColumn, changeColumn and addIndex, but nothing works. I don't know if my approach is correct or not, please help.
Here my migration approach
"use strict";
module.exports = {
async up(queryInterface, Sequelize) {
/**
* Add altering commands here.
*
* Example:
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
// *********** received duplicate error ***********
// await queryInterface.addColumn("Products", "sku", {
// unique: true,
// type: Sequelize.STRING,
// allowNull: false,
// validate: {
// notNull: {
// msg: "Product SKU cannot be empty",
// },
// notEmpty: {
// msg: "Product SKU cannot be empty",
// },
// },
// });
// await queryInterface.addColumn("Products", "barcode", {
// unique: true,
// type: Sequelize.STRING,
// allowNull: false,
// validate: {
// notNull: {
// msg: "Product barcode cannot be empty",
// },
// notEmpty: {
// msg: "Product barcode cannot be empty",
// },
// },
// });
// ***************** received Cannot create property 'fields' ***********
// await queryInterface.addIndex("Products", "sku", {
// unique: true,
// });
// await queryInterface.addIndex("Products", "barcode", {
// unique: true,
// });
// **************** received Validation error *************
await queryInterface.changeColumn("Products", "sku", {
unique: true,
type: Sequelize.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product SKU cannot be empty",
},
notEmpty: {
msg: "Product SKU cannot be empty",
},
},
});
await queryInterface.changeColumn("Products", "barcode", {
unique: true,
type: Sequelize.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product barcode cannot be empty",
},
notEmpty: {
msg: "Product barcode cannot be empty",
},
},
});
},
async down(queryInterface, Sequelize) {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
// await queryInterface.removeColumn("Products", "sku");
// await queryInterface.removeColumn("Products", "barcode");
},
};
Here how my model looks like:
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Product 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
Product.belongsTo(models.Brand, {
foreignKey: "brandId",
});
Product.belongsTo(models.Category, {
foreignKey: "categoryId",
as: "Category",
});
Product.belongsTo(models.Category, {
foreignKey: "subcategoryId",
as: "Subcategory",
});
Product.belongsToMany(models.Tag, {
through: models.ProductTag,
foreignKey: "productId",
});
Product.hasMany(models.ProductInfo, {
foreignKey: "productId",
});
Product.hasMany(models.ProductPhoto, {
foreignKey: "productId",
});
Product.hasMany(models.ProductStockHistory, {
foreignKey: "productId",
});
Product.hasMany(models.Trolley, {
foreignKey: "productId",
targetKey: "id",
});
Product.hasMany(models.Wishlist, {
foreignKey: "productId",
targetKey: "id",
});
Product.belongsToMany(models.PromoPrice, {
through: models.ProductPromoPrice,
foreignKey: "ProductId",
});
Product.hasOne(models.ProductPromoPrice, {
foreignKey: "ProductId",
as: "SpecialPrice",
});
}
}
Product.init(
{
sku: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product SKU cannot be empty",
},
notEmpty: {
msg: "Product SKU cannot be empty",
},
},
},
barcode: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product barcode cannot be empty",
},
notEmpty: {
msg: "Product barcode cannot be empty",
},
},
},
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product name cannot be empty",
},
notEmpty: {
msg: "Product name cannot be empty",
},
},
},
description: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
notNull: {
msg: "Product description cannot be empty",
},
notEmpty: {
msg: "Product description cannot be empty",
},
},
},
brandId: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "Product brandId cannot be empty",
},
notEmpty: {
msg: "Product brandId cannot be empty",
},
},
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "Product categoryId cannot be empty",
},
notEmpty: {
msg: "Product categoryId cannot be empty",
},
},
},
subcategoryId: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "Product subcategoryId cannot be empty",
},
notEmpty: {
msg: "Product subcategoryId cannot be empty",
},
},
},
unit: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product unit cannot be empty",
},
notEmpty: {
msg: "Product unit cannot be empty",
},
},
},
price: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "Product price cannot be empty",
},
notEmpty: {
msg: "Product price cannot be empty",
},
},
},
notes: DataTypes.STRING,
photoURL: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: "Product photo URL cannot be empty",
},
notEmpty: {
msg: "Product photo URL cannot be empty",
},
isUrl: {
msg: "Invalid product photo URL",
},
},
},
videoURL: {
type: DataTypes.STRING,
validate: {
isUrl: {
msg: "Invalid product video URL",
},
},
},
isActive: {
type: DataTypes.BOOLEAN,
allowNull: false,
validate: {
notNull: {
msg: "Product isActive cannot be empty",
},
notEmpty: {
msg: "Product isActive cannot be empty",
},
},
},
stock: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "Product stock cannot be empty",
},
notEmpty: {
msg: "Product stock cannot be empty",
},
},
},
reservedStock: DataTypes.INTEGER,
sold: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
firestoreId: DataTypes.STRING,
buyPrice: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
},
{
hooks: {
beforeCreate(Product, options) {
Product.stock = Product.stock || 0;
Product.reservedStock = 0;
},
},
sequelize,
modelName: "Product",
// edited here
paranoid: true,
}
);
return Product;
};
I just need to add unique: true in sku & barcode, please help. Thanks
EDITED
here is what I received in terminal when I use addConstraint
Refer to the queryInterface API here addConstraint removeConstraint
A transaction was made to ensure that all the queries succeed together. The name field in the second parameter of queryInterface.addConstraint can be anything. If any two columns have the same name of constraint, then a composite constraint will be created.
module.exports = {
async up(queryInterface, Sequelize) {
return queryInterface.sequelize.transaction((transaction) => {
return Promise.all([
queryInterface.addConstraint("Products", {
fields: ["barcode"],
type: "unique",
name: "Products_barcode_uk",
transaction,
}),
queryInterface.addConstraint("Products", {
fields: ["sku"],
type: "unique",
name: "Products_sku_uk",
transaction,
}),
]);
});
},
async down(queryInterface, Sequelize) {
return queryInterface.sequelize.transaction((transaction) => {
return Promise.all([
queryInterface.removeConstraint("Products", "Products_barcode_uk", {
fields: ["barcode"],
transaction,
}),
queryInterface.removeConstraint("Products", "Products_sku_uk", {
fields: ["sku"],
transaction,
}),
]);
});
},
};

Sequelize validation doesn't work on PUT method

I'm pretty new to Sequelize. Here's a model that I have. The validation seems to be working fine on POST method, but it doesn't work on PUT method. I'm not sure what I'm missing here. I'd appreciate all the help guys.
const Sequelize = require('sequelize');
module.exports = (sequelize) => {
class Course extends Sequelize.Model {}
Course.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: Sequelize.STRING,
allowNull: false,
validate: {
notNull: {
msg: 'Please provide a value for "title"'
},
notEmpty: {
msg: 'Please provide a value for "title"'
}
}
},
description: {
type: Sequelize.TEXT,
allowNull: false,
validate: {
notNull: {
msg: 'Please provide a value for "description"'
},
notEmpty: {
msg: 'Please provide a value for "description"'
}
}
}
}, { sequelize });
Course.associate = (models) => {
Course.belongsTo(models.User, {
foreignKey: {
fieldName: 'userId',
allowNull: false
}
})
};
return Course
}
Here's my code on main route.
router.put('/courses/:id', asyncHandler(async(req, res) => {
try {
const course = await Course.findByPk(req.params.id)
await course.update(req.body);
res.status(204).end();
} catch (error) {
console.error(error);
}
}));

Sequelize mixes camel case and snake case keys in result object

Am trying to get the resulting plain object from Sequelize create (or any Sequelize query) to be snake case keys throughout. The result objects are mixed however. Here's an example query:
const object = await models.Account.create({
userId,
name,
accountNumber
})
console.log(object.get({ plain: true }))
The result object is mixed keys of camel case and snake case:
{
"id": 2,
"userId": 1,
"name": "baller",
"accountNumber": "1234-123-1234",
"updated_at": "2019-01-07T02:23:41.305Z",
"created_at": "2019-01-07T02:23:41.305Z",
"balance": "0.00",
"deleted_at": null
}
Any idea how to get the result plain object or nested objects to be completely snake case keys only? Upgrade sequelize from ^4.42.0 to ^5.0.0-beta in package.json and happens for both. Not sure what else to try?
Accounts table is all snake case column names:
return queryInterface.createTable('accounts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
user_id: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
name: {
allowNull: false,
type: Sequelize.STRING(50)
},
account_number: {
allowNull: false,
type: Sequelize.STRING(50)
},
Account model has has option underscored: true and camelCase attrs with field in snake case
const Account = sequelize.define('Account', {
userId: {
type: DataTypes.INTEGER,
field: 'user_id',
allowNull: false,
validate: {
notNull(value) {
if (value == null) {
throw new Error('Missing user id')
}
}
}
},
name: {
type: DataTypes.STRING,
field: 'name',
validate: {
notEmpty: true
}
},
accountNumber: {
type: DataTypes.STRING,
field: 'account_number',
validate: {
notEmpty: true
}
},
}, {
tableName: 'accounts',
paranoid: true,
underscored: true
})
For now I'm resolving it with:
import snakeCaseKeys from 'snakecase-keys'
let jsonObject = object.get({ plain: true })
jsonObject = snakeCaseKeys(jsonObject)

SailsJs 0.10: User.findOne({_id: id}) returning null for unique primary key

I have the following user model:
var model = module.exports = {
autoPK: false,
attributes: {
id: {
type: 'string',
primaryKey: true
},
email: {
type: 'string',
required: true
},
hash: {
type: 'string',
required: true
},
}
}
And the following query on it:
User.findOne({_id: req.param('user_id')}).populate('drafts').then(function(user) {
console.log("USER: " + JSON.stringify(user) + " FOR: " + req.param('user_id'));
res.send(user.drafts, 200);
});
From the print statement, I know nothing is turning being returned for the ID "Rfrq8un5f," but the mongodb command line outputs this:
> db.user.find();
{ "email" : "m#m.com", "hash" : "[...]", "createdAt" : ISODate("2014-05-18T16:32:21.023Z"), "updatedAt" : ISODate("2014-05-18T16:32:21.023Z"), "_id" : "9PTIqHxEc" }
What's going on?
To solve the id: null when use waterline with mongo adapter you must add to the model: autoPK: false, schema: true. You need both, isn't enough with autoPK false or schema true.
This is a model example solving that issue (user model):
module.exports = {
schema: true,
autoPK: false,
attributes: {
name: {
type: 'string',
required: true
},
email: {
type: 'string',
email: true,
required: true,
unique: true
},
password: {
type: 'string',
minLength: 6,
maxLength: 15,
columnName: 'encrypted_password',
required: true
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
},
beforeCreate: function(values, next) {
require('bcrypt').hash(values.password, 10, function passwordEncrypted(err, encryptedPassword) {
if(err) console.log(err);
values.password = encryptedPassword;
next();
});
}
};

Mongoose. Update by document id throws [TypeError: Cannot read property '_id' of undefined]

There is my code:
var fileModel = context.models.File,
query = {
_id: context.models.ObjectId("532083358ab1654c0c8b4ced") // TODO: for debug, change after update fix
},
update = {
description: context.data.description,
userId: context.data.userId ?
context.models.ObjectId(context.data.userId) : undefined,
isAdded: true
};
fileModel.update(query, update, { multi: true }, function (err) {
if (err) {
console.log('update');
console.log(err);
context.sendJson({ success: false, err: err });
}
else {
context.sendJson({ success: true });
}
});
There is my Schema:
var fileSchema = new schema({
path: { type: String, required: true, validate: [validateName, 'a path is required'] },
isApproved: { type: Boolean, default: false },
isAdded: { type: Boolean, default: false },
name: { type: String, required: true, validate: [validateName, 'a name is required'] },
description: { type: String },
userId: { type: schema.Types.ObjectId },
updated: { type: Date, default: Date.now },
size: { type: Number }
}, { autoIndex: false });
When I try to update document by id I see this messages in console:
update
[TypeError: Cannot read property '_id' of undefined]
I think problem in
userId: context.data.userId ?
context.models.ObjectId(context.data.userId) : undefined,
But I don't understand how fix it.
I solve this by separate part of my code. But I can't understand what's wrong in my first solution. That's working code:
var fileModel = context.models.File,
query = {
_id: {
$in: context.data.files.map(function (el) {
return context.models.ObjectId(el);
})
}
},
update = {
description: context.data.description,
isAdded: true
};
if (context.data.userId){
update.userId = context.models.ObjectId(context.data.userId);
}
fileModel.update(query, update, { multi: true }, function (err) {
if (err) {
console.log('update');
console.log(err);
context.sendJson({ success: false, err: err });
}
else {
context.sendJson({ success: true });
}
});

Categories

Resources