Validation & constraints

Read this page in the documentation

Validation & constraints prorm validates records before they hit the database. You attach validation rules to a model's attributes, and the ORM runs them automatically on create, save, and bulkCreate. When a record fails, prorm throws a PrormValidationError carrying a structured list of every failure. This guide covers the built-in rule set, custom rules, the allowNull/unique constraints, model-level validation, when validation fires, and the exact shape of the error you get back. Validation lives in src/validators/validator.ts. It is wired into models through Model.create, instance.save, and instance.validate in src/models/model.ts. Field-level validators Attach a validate object to any attribute. Each key is a rule name and each value is the rule's argument. The value is boolean | string | number | RegExp | array. A boolean-style rule (isEmail, notEmpty, isInt, …) is turned on with true. A parameterized rule takes its argument directly as the value: len takes a number or a [min, max] tuple, min/max take a number, isIn/notIn take an array, matches takes a RegExp. Setting a rule value to false disables it, which is handy when overriding a base configuration: Custom error messages Pass a string instead of true to supply your own message. The rule still runs; the string is only used as the failure message. Note that isIn and notIn treat a string value as data, not as a message — always pass those an array. The built-in validator set Every rule below is implemented as a static method on the Validator class and dispatched by name. Rules skip null, undefined, and empty-string values (use notEmpty to require a value), so an optional field only validates when present. Format and content: isEmail — valid email address isUrl — parseable URL isIP — IPv4 or IPv6 address isAlpha — letters only isAlphanumeric — letters and digits only isNumeric — digits only isInt — integer isFloat — floating-point number isDecimal — decimal number (123.45) isBoolean — boolean-ish value isDate — parseable date isTime — HH:MM or HH:MM:SS isDateString — ISO 8601 date string isUUID — UUID v1/v4/v5 isJSON — valid JSON value or string isCreditCard — passes the Luhn check isPostalCode — postal code (Validator.isPostalCode(value, locale), default US) isMobilePhone — phone number (default locale en-US) isHexColor — hex color such as #ff8800 isLowercase / isUppercase — case check Presence, length, and range: notEmpty — rejects null, undefined, empty string, empty array isEmpty — the inverse len — len: 8 for an exact length, len: [3, 20] for a range min / max — numeric bounds range — range: [1, 10] Sets, equality, and patterns: isIn: [...] — value must be one of the listed values notIn: [...] — value must not be in the list equals / notEquals — strict equality against the rule value matches: /pattern/ — value matches the RegExp (string patterns are compiled) notMatches: /pattern/ — value must not match Where validation happens Two distinct layers. Validators run in the process and never reach the database — a ValidationError means no statement was sent. Constraints run in the database and produce a different error class. A uniqueness rule can only be truly enforced by the second: between a validator's SELECT and the INSERT, another writer can take the value. Custom validators For logic outside the built-in set, use matches with your own regular expression, or run rules programmatically with the Validator class and the standalone validate / validateField helpers. These are the same functions the model uses internally, so they are always available. To enforce rules that span several fields or need to run I/O, put the logic in a beforeValidate (or beforeSave) hook and throw when it fails. A throw from the hook aborts the write before it reaches the database: Uniqueness There are two independent notions of "unique": The unique attribute option is a database constraint applied when the table is created or synced. It does not run during application-side validation. For an application-level uniqueness check that queries the database, use the isUnique validation rule. Because it needs an async lookup, it only runs when validation is executed asynchronously — which is exactly what create, save, and the instance validate() method do. The synchronous entry points (validate, validateField, isValid, validateSync) cannot query the database and always pass isUnique, logging a warning instead. isUnique resolves its target model either from a model registered with registerValidatorModel(name, model) or from a live model reference passed as options.model. When validation runs on an update, prorm automatically excludes the current record (by primary key) so a row does not conflict with itself. Model-level validation Pass a validate map in the model options to attach extra rule configs to fields. Each entry is merged into that field's rules under a validators key, so it composes with the attribute-level validate. When validation runs Validation is automatic on writes: Model.create(values) validates before inserting. instance.save() validates before an insert or update. Model.bulkCreate(rows) validates each row. You control it with these switches: Per call: pass { validate: false } to create / save to skip it. Per model: validateOnInsert: false skips validation on inserts, validateOnUpdate: false skips it on updates. Globally for the model: skipValidations: true turns off all validation. You can also validate on demand without writing: The ValidationError shape When an automatic write fails validation, prorm throws an error whose name is 'PrormValidationError'. Its message is the message of the first failure, and its errors property is the full list. Each entry in errors is a ValidationErrorItem: The exported ValidationError class (used by the standalone helpers via createValidationError) offers a few conveniences on top of a plain error: Collecting messages for a form is straightforward: Next: Hooks — running your own logic around the lifecycle. Related reading Hooks — logic around the lifecycle, once values are valid Error handling — what a validation failure looks like when it throws