Marco Jr
Marco Jr

Reputation: 6806

How to populate a database default value when inserting with sequelize?

On my schema, I've this...

const userSchema = db.define('USER',
  {
    id: { type: Sequelize.UUIDV4, allowNull: false, primaryKey: true},
   ...
   }

On my MSSQL database, I've this:

id uniqueidentifier  NOT NULL DEFAULT NEWID(),

so, NEWID() will populate the id with a new UUIDV4 data. However, when I perform a User.create(...), this throw me a ValidationError [SequelizeValidationError]: notNull Violation: USER.id cannot be null

Then I decided to make an attempt:

 hooks : {
        beforeCreate(user, options) {
             user.id = generateNewUUID()
        }
     }

but it's also not working because hooks is just executed after the schema validation.

is there some work around for this that's not involves placing the id as a parameter of User.create ? It's something I'd like to avoid.

Upvotes: 1

Views: 2886

Answers (2)

defraggled
defraggled

Reputation: 1218

Other commenters have observed OP might have been playing with a primary key, which has special behaviour around validation. But the title asks about 'values' generally. The common advice on that question is:

  1. Remove the allowNull requirement on the model definition, or
  2. Use a hook to manually insert the default.

These are not great answers. Option 1 is effectively permanently distorting your model (and validations). Option 2 is closer to correct, but still annoying and unintuitive: what's the point in setting a default if I have to build logic to apply it?

For the record: if you did want to go with Option 2, beforeCreate will not help (as you have noticed) because that hook is called after validation. You would have to use the beforeValidate hook-- and then specifically add logic to ensure the hook is only triggered upon the record creation. Like so:

hooks: {
    beforeValidate: async (user) => {
        if (user.isNewRecord) await applyDefaults(user);
    },
}

This^ works. But here's the real solution:

Don't write any hooks. Define your default value on the model like so:

const userSchema = db.define('USER', {
    id: {
        type: Sequelize.UUIDV4,
        allowNull: false,
        primaryKey: true,
        defaultValue: generateNewUUID()
}})

Then, when creating instances, use Model.build() instead of Model.create(). The former applies the defaults properly, before validation. Eg:

var user = User.build({
    email: "[email protected]"
})
user = await user.save()
console.log(user.id) // <--- Now works as expected

CAVEAT: At time of writing, Sequelize still doesn't seem to support promises for default values. If you need to apply a resolved promise as defaultValue, you're stuck with Options 1 or 2.

Upvotes: 1

mohdule
mohdule

Reputation: 538

You need to remove the { allowNull: false } constraint in order to use a default value from the DBMS side.

But if you want to keep the { allowNull: false } constraint, you can alternatively set the default value to UUIDV4 from the ORM side by setting the default value of the column to Sequelize.UUIDV4 like so:

const userSchema = db.define('USER',
  {
    id: {
       type: Sequelize.UUID,
       defaultValue: Sequelize.UUIDV4,
       allowNull: false,
       primaryKey: true
    },
   ...
   }

(you don't need a beforeCreate hook in this case)

Upvotes: 0

Related Questions