Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize validator only returning one error at a time

Upon creating or updating items in my database using Sequelize, I find that the validator only returns a single validation error at a time.

This is not ideal, since I'd like to return all errors to the user when they submit the form that has multiple errors in it.

This is my model:

module.exports = (Sequelize, DataTypes) => {
  const User = Sequelize.define(
    'user',
    {
      email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: {
          args: true,
          msg: 'That email is not available.'
        },
        validate: {
          isEmail: {
            args: true,
            msg: 'Please enter a valid email.'
          }
        }
      },
      username: {
        type: DataTypes.STRING,
        allowNull: false,
        notEmpty: true,
        unique: {
          args: true,
          msg: 'That username is not available.'
        },
        validate: {
          min: {
            args: USER_USERNAME_MIN_LENGTH,
            msg: 'Please choose a longer username.'
          },
          max: {
            args: USER_USERNAME_MAX_LENGTH,
            msg: 'Please choose a shorter username.'
          },
          is: {
            args: /^[0-9a-zA-Z](\/?[a-zA-Z0-9-_])*\/?$/i,
            msg: 'Please enter a valid username.'
          }
        }
      }
    },
    {
      underscored: true,
    }
  );

  return User;
};

This is my controller (in this case, I'm trying to update a user, say, User 1 and modify their values for username and email which just so happen to be the same exact values for another user, say User 2).

try {
  const user = await User.findById(req.user.id);
  if (user) {
    const updatedUser = await user.update({
      username: username || req.user.username,
      email: email || req.user.email
    });

    res.status(200).end();
  }
} catch (err) {
  console.log(err);
  return res.status(500).send(getMappedErrors(err));
}

So you would expect the validator to return errors for both the email and username field since it violates the unique flag.

However, this is what I get in that catch block:

{ SequelizeUniqueConstraintError: That username is not available.
    at Query.formatError (/site/node_modules/sequelize/lib/dialects/postgres/query.js:325:18)
    at query.catch.err (/site/node_modules/sequelize/lib/dialects/postgres/query.js:86:18)
    at tryCatcher (/site/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/site/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/site/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/site/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/site/node_modules/bluebird/js/release/promise.js:689:18)
    at Async._drainQueue (/site/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/site/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/site/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:763:18)
    at tryOnImmediate (timers.js:734:5)
    at processImmediate (timers.js:716:5)
    at process.topLevelDomainCallback (domain.js:101:23)
  name: 'SequelizeUniqueConstraintError',
  errors:
   [ ValidationErrorItem {
       message: 'That username is not available.',
       type: 'unique violation',
       path: 'username',
       value: 'bob',
       origin: 'DB',
       instance: [user],
       validatorKey: 'not_unique',
       validatorName: null,
       validatorArgs: [] } ],
  fields: { username: 'bob' },
  parent:
   { error: duplicate key value violates unique constraint "users_username_key"
    at Connection.parseE (/site/node_modules/pg/lib/connection.js:545:11)
    at Connection.parseMessage (/site/node_modules/pg/lib/connection.js:370:19)
    at Socket.<anonymous> (/site/node_modules/pg/lib/connection.js:113:22)
    at Socket.emit (events.js:127:13)
    at Socket.emit (domain.js:421:20)
    at addChunk (_stream_readable.js:269:12)
    at readableAddChunk (_stream_readable.js:256:11)
    at Socket.Readable.push (_stream_readable.js:213:10)
    at TCP.onread (net.js:598:20)
     name: 'error',
     length: 204,
     severity: 'ERROR',
     code: '23505',
     detail: 'Key (username)=(bob) already exists.',
     hint: undefined,
     position: undefined,
     internalPosition: undefined,
     internalQuery: undefined,
     where: undefined,
     schema: 'public',
     table: 'users',
     column: undefined,
     dataType: undefined,
     constraint: 'users_username_key',
     file: 'nbtinsert.c',
     line: '434',
     routine: '_bt_check_unique',
     sql: 'UPDATE "users" SET "username"=\'bob\',"email"=\'[email protected]\',"updated_at"=\'2018-03-28 19:26:27.341 +00:00\' WHERE "id" = 2' } ...

I truncated the rest of it, but as you can see, it only returns the error for the username. When I fix that, and then submit my form again, only then do I get the validation error for the email address.

like image 240
bob_cobb Avatar asked Aug 15 '26 12:08

bob_cobb


1 Answers

I had the same issue.

If you have the allowNull:false constraint set on a property and your field for that property is null, all validators will be skipped and a ValidationError will be thrown. So db constraints get checked first, one by one, after which the regular validation is performed, where all the errors are returned in an array as expected.

Here is a reference to the above mentioned

like image 87
chen7david Avatar answered Aug 18 '26 01:08

chen7david



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!