The Basics
Validation
Introduction
Input validation uses validatorjs with JCC-specific async rules. Validate from the request with req.validate(rules, customMessages?), or use the global validate(rules, customMessages?) helper during a request.
When a controller action is injected with a FormRequest subclass, the container constructs it with the current req, then await requestInstance.rules() before your method runs. Implement async rules() and call await this.validate({ ... }) there so validation failures never reach the action (see Controllers.md and Request.md).
On failure, the framework flashes old input (the request body) and validation errors, then throws ValidationException. The error handler turns that into 422 responses: JSON for API-style requests, redirectBack() for typical browser forms.
Basic usage
Rules are an object: each key is a field name (matching req.body keys unless you use file rules), each value is a string or array of rule tokens (arrays work best with JCC’s normalizeBody for sometimes, nullable, file, and image).
Custom messages (optional second argument):
After a successful run, req.validated is set to a function that returns the validated body (currently req.body). Validator.validate also resolves with that same object, so you can use either the return value or req.validated().
Use await on validate because the engine runs checkAsync. req.validated() itself is synchronous.
Global helper
Same behavior as req.validate, but uses the current request from the container (only valid during an HTTP request).
Failed validation and the UI
When validation fails, Validator.validate flashes:
old— copy ofreq.bodyfor repopulating forms.VALIDATION_ERROR— error payload for templates.
HttpRequest maps the latest flash into res.locals.errors and res.locals.old on the next render so Blade can show field errors (see Request and JCC Blade). For JSON clients (expectsJson() and not Inertia), the framework responds with 422 and an errors payload.
Rule modifiers
JCC adjusts request data before rules run for these modifiers:
sometimes— If the field is missing from the body, that field can be skipped (optional presence).nullable— Missing field becomes""so nullable-style rules can run consistently.file/image— Value is taken from uploaded files (req.hasFile,req.file) instead of raw body text.
Use these in the array form, for example:
Supported validation rules
The project depends on validatorjs. JCC runs validation through checkAsync, so every rule name is resolved in an async pipeline. The framework also registers additional async rules for database and file checks (see JCC custom rules below).
Built-in rules from validatorjs
Presence and conditionals
required— Value is present and not empty after trimming whitespace.sometimes— Always passes; combined with JCCnormalizeBody, missing fields can be omitted from the normalized input so other rules are skipped for that key (see Rule modifiers in JCC).present— Key must exist in input; value may be empty.required_if:otherField,value— Required whenotherFieldequalsvalue.required_unless:otherField,value— Required unlessotherFieldequalsvalue.required_with:foo,bar,...— Required if any of the listed fields are present.required_with_all:foo,bar,...— Required if all listed fields are present.required_without:foo,bar,...— Required if any of the listed fields are absent.required_without_all:foo,bar,...— Required if all listed fields are absent.
Strings and text shape
string— Value is a string.email— Email-shaped string.url— Valid URL format.alpha— Letters only.alpha_num— Letters and numbers only.alpha_dash— Letters, numbers, dashes, underscores.hex— Hexadecimal characters.regex:pattern— Matches regex; prefer an array of rules if the pattern contains|. Escape backslashes for regex strings.
Numbers and digits
numeric— Numeric (string forms allowed).integer— Integer value.digits:n— Numeric string with exactlyndigits.digits_between:min,max— Digit length betweenminandmax(see library behavior for empty values).
Size and bounds (length for strings, value for numbers; see library getSize)
min:n,max:n,size:n,between:min,max.
Sets and equality
in:foo,bar,...— Value must be one of the comma-separated options (also supports object/array rule form in rawvalidatorjsAPIs).not_in:foo,bar,...— Value must not be in the list.same:otherField— Must equalotherField.different:otherField— Must differ fromotherField.confirmed— Expects{field}_confirmationmatchingfield(e.g.password/password_confirmation).
Other scalars
boolean—true/false/0/1and common string forms.array— Value is an array.accepted—yes,on,1, ortrue(typical “accept terms” check).
Dates (parsed with JavaScript Date; library includes extra date sanity checks)
dateafter:date,after_or_equal:datebefore:date,before_or_equal:date
IP addresses
ip— IPv4 or IPv6.ipv4,ipv6
Official behavior, edge cases, nested keys, and wildcards (e.g. users.*.email) are described in the validatorjs README on GitHub.
JCC custom rules
These are registered on every Validator.validate call alongside validatorjs:
nullable— Async rule that always callspasses(); meaningful use is together withnormalizeBody, which injects""when the field is missing so other rules can treat “empty” consistently.unique:modelorunique:model,column— No row may already have this value.modelis resolved viagetModelFile;columndefaults to the attribute name. Implemented withexistsHelperagainst your ORM (Lucid / Sequelize / Mongoose).exists:modelorexists:model,column— A row must exist for this value. Fails with “required” if the value is empty.file— After normalization, the value reflects upload presence; fails if falsy (“The file field is required.”).image— Same idea for image uploads (“The image field is required.”).
Use array rule lists for sometimes, nullable, file, and image so JCC’s normalizeBody can detect them (see Rule modifiers in JCC).
ValidationException
Thrown when checkAsync fails. The exception carries a flattened errors map (field → first message) for the handler. Validator.validate already flashes old and VALIDATION_ERROR before rejecting; the error handler may flash again when handling ValidationException for web flows. Do not catch and swallow the exception unless you intend to replace the default 422 / redirect behavior. For how these errors become HTTP responses (JSON vs redirectBack, status codes), see Error handling.
FormRequest example
Use this.req.validated() only after this.validate(...) has completed successfully (for example inside the same class method). There is no this.validated() on FormRequest itself.
FormRequest messages
Override protected messages(): Record<string, string> on your request class. FormRequest.validate passes this.messages() as the second argument to req.validate, so custom keys follow validatorjs message conventions.
Key format: "rule.field" targets one field; bare "rule" is a fallback for all fields that fail that rule.
Summary
- Validate with
await req.validate({ field: ["rule", ...] }, messages?)orawait validate(...). - Prefer arrays of rules when using
sometimes,nullable,file, andimage. unique/existstie into your models;ValidationExceptionproduces 422 JSON orredirectBackwith flashedold/ errors.FormRequest: implementasync rules()withawait this.validate({ ... }); the container runsrules()before your controller method when the request type is injected (Controllers.md). After success, usereq.validated()or thevalidatereturn value (Request.md).