Validations

Model validations allow you to specify format/content/inheritance validations for each attribute of the model.

Validations are automatically run on create, update and save. You can also call validate() to manually validate an instance.

Per-attribute validations

You can define your custom validators or use several built-in validators, implemented by validator.js, as shown below.

  1. class ValidateMe extends Model {}
  2. ValidateMe.init({
  3. bar: {
  4. type: Sequelize.STRING,
  5. validate: {
  6. is: ["^[a-z]+$",'i'], // will only allow letters
  7. is: /^[a-z]+$/i, // same as the previous example using real RegExp
  8. not: ["[a-z]",'i'], // will not allow letters
  9. isEmail: true, // checks for email format (foo@bar.com)
  10. isUrl: true, // checks for url format (http://foo.com)
  11. isIP: true, // checks for IPv4 (129.89.23.1) or IPv6 format
  12. isIPv4: true, // checks for IPv4 (129.89.23.1)
  13. isIPv6: true, // checks for IPv6 format
  14. isAlpha: true, // will only allow letters
  15. isAlphanumeric: true, // will only allow alphanumeric characters, so "_abc" will fail
  16. isNumeric: true, // will only allow numbers
  17. isInt: true, // checks for valid integers
  18. isFloat: true, // checks for valid floating point numbers
  19. isDecimal: true, // checks for any numbers
  20. isLowercase: true, // checks for lowercase
  21. isUppercase: true, // checks for uppercase
  22. notNull: true, // won't allow null
  23. isNull: true, // only allows null
  24. notEmpty: true, // don't allow empty strings
  25. equals: 'specific value', // only allow a specific value
  26. contains: 'foo', // force specific substrings
  27. notIn: [['foo', 'bar']], // check the value is not one of these
  28. isIn: [['foo', 'bar']], // check the value is one of these
  29. notContains: 'bar', // don't allow specific substrings
  30. len: [2,10], // only allow values with length between 2 and 10
  31. isUUID: 4, // only allow uuids
  32. isDate: true, // only allow date strings
  33. isAfter: "2011-11-05", // only allow date strings after a specific date
  34. isBefore: "2011-11-05", // only allow date strings before a specific date
  35. max: 23, // only allow values <= 23
  36. min: 23, // only allow values >= 23
  37. isCreditCard: true, // check for valid credit card numbers
  38. // Examples of custom validators:
  39. isEven(value) {
  40. if (parseInt(value) % 2 !== 0) {
  41. throw new Error('Only even values are allowed!');
  42. }
  43. }
  44. isGreaterThanOtherField(value) {
  45. if (parseInt(value) <= parseInt(this.otherField)) {
  46. throw new Error('Bar must be greater than otherField.');
  47. }
  48. }
  49. }
  50. }
  51. }, { sequelize });

Note that where multiple arguments need to be passed to the built-in validation functions, the arguments to be passed must be in an array. But if a single array argument is to be passed, for instance an array of acceptable strings for isIn, this will be interpreted as multiple string arguments instead of one array argument. To work around this pass a single-length array of arguments, such as [['one', 'two']] as shown above.

To use a custom error message instead of that provided by validator.js, use an object instead of the plain value or array of arguments, for example a validator which needs no argument can be given a custom message with

  1. isInt: {
  2. msg: "Must be an integer number of pennies"
  3. }

or if arguments need to also be passed add an args property:

  1. isIn: {
  2. args: [['en', 'zh']],
  3. msg: "Must be English or Chinese"
  4. }

When using custom validator functions the error message will be whatever message the thrown Error object holds.

See the validator.js project for more details on the built in validation methods.

Hint: You can also define a custom function for the logging part. Just pass a function. The first parameter will be the string that is logged.

Per-attribute validators and allowNull

If a particular field of a model is set to not allow null (with allowNull: false) and that value has been set to null, all validators will be skipped and a ValidationError will be thrown.

On the other hand, if it is set to allow null (with allowNull: true) and that value has been set to null, only the built-in validators will be skipped, while the custom validators will still run.

This means you can, for instance, have a string field which validates its length to be between 5 and 10 characters, but which also allows null (since the length validator will be skipped automatically when the value is null):

  1. class User extends Model {}
  2. User.init({
  3. username: {
  4. type: Sequelize.STRING,
  5. allowNull: true,
  6. validate: {
  7. len: [5, 10]
  8. }
  9. }
  10. }, { sequelize });

You also can conditionally allow null values, with a custom validator, since it won't be skipped:

  1. class User extends Model {}
  2. User.init({
  3. age: Sequelize.INTEGER,
  4. name: {
  5. type: Sequelize.STRING,
  6. allowNull: true,
  7. validate: {
  8. customValidator(value) {
  9. if (value === null && this.age !== 10) {
  10. throw new Error("name can't be null unless age is 10");
  11. }
  12. })
  13. }
  14. }
  15. }, { sequelize });

You can customize allowNull error message by setting the notNull validator:

  1. class User extends Model {}
  2. User.init({
  3. name: {
  4. type: Sequelize.STRING,
  5. allowNull: false,
  6. validate: {
  7. notNull: {
  8. msg: 'Please enter your name'
  9. }
  10. }
  11. }
  12. }, { sequelize });

Model-wide validations

Validations can also be defined to check the model after the field-specific validators. Using this you could, for example, ensure either neither of latitude and longitude are set or both, and fail if one but not the other is set.

Model validator methods are called with the model object's context and are deemed to fail if they throw an error, otherwise pass. This is just the same as with custom field-specific validators.

Any error messages collected are put in the validation result object alongside the field validation errors, with keys named after the failed validation method's key in the validate option object. Even though there can only be one error message for each model validation method at any one time, it is presented as a single string error in an array, to maximize consistency with the field errors.

An example:

  1. class Pub extends Model {}
  2. Pub.init({
  3. name: { type: Sequelize.STRING },
  4. address: { type: Sequelize.STRING },
  5. latitude: {
  6. type: Sequelize.INTEGER,
  7. allowNull: true,
  8. defaultValue: null,
  9. validate: { min: -90, max: 90 }
  10. },
  11. longitude: {
  12. type: Sequelize.INTEGER,
  13. allowNull: true,
  14. defaultValue: null,
  15. validate: { min: -180, max: 180 }
  16. },
  17. }, {
  18. validate: {
  19. bothCoordsOrNone() {
  20. if ((this.latitude === null) !== (this.longitude === null)) {
  21. throw new Error('Require either both latitude and longitude or neither')
  22. }
  23. }
  24. },
  25. sequelize,
  26. })

In this simple case an object fails validation if either latitude or longitude is given, but not both. If we try to build one with an out-of-range latitude and no longitude, raging_bullock_arms.validate() might return

  1. {
  2. 'latitude': ['Invalid number: latitude'],
  3. 'bothCoordsOrNone': ['Require either both latitude and longitude or neither']
  4. }

Such validation could have also been done with a custom validator defined on a single attribute (such as the latitude attribute, by checking (value === null) !== (this.longitude === null)), but the model-wide validation approach is cleaner.