> adonis install @adonisjs/validator
You are viewing the legacy version of AdonisJS. Visit https://adonisjs.com for newer docs. This version will receive security patches until the end of 2021.
AdonisJs makes it simple to validate user input with the help of a dedicated validation provider.
In this guide you learn how to validate data manually or via route validators.
AdonisJs validation uses Indicative under the hood. For full usage details, see the official Indicative documentation. |
Follow the below instructions to set up the validation provider.
First, run the adonis
command to download the validator provider:
> adonis install @adonisjs/validator
Then, register the validator provider inside the start/app.js
file:
const providers = [
'@adonisjs/validator/providers/ValidatorProvider'
]
Let’s start with the example of validating user input received via HTML form:
<form method="POST" action="{{ route('UserController.store') }}">
<div>
<input type="text" name="email" />
</div>
<div>
<input type="text" name="password" />
</div>
<button type="submit"> Submit </button>
</form>
Register the route and controller to handle the form submission and use the validator to validate the data:
Route.post('users', 'UserController.store')
const { validate } = use('Validator')
class UserController {
async store ({ request, session, response }) {
const rules = {
email: 'required|email|unique:users,email',
password: 'required'
}
const validation = await validate(request.all(), rules)
if (validation.fails()) {
session
.withErrors(validation.messages())
.flashExcept(['password'])
return response.redirect('back')
}
return 'Validation passed'
}
}
module.exports = UserController
Let’s break down the above controller code into small steps:
We defined our rules
schema.
We used the validate
method to validate all request data against our rules.
If validation fails, we flash all errors and redirect back to our form.
We can modify the HTML form to display our flash messages, which are set when validation fails:
<form method="POST" action="{{ route('UserController.store') }}">
<div>
<input type="text" name="email" value="{{ old('email', '') }}" />
{{ elIf('<span>$self</span>', getErrorFor('email'), hasErrorFor('email')) }}
</div>
<div>
<input type="text" name="password" />
{{ elIf('<span>$self</span>', getErrorFor('password'), hasErrorFor('password')) }}
</div>
<button type="submit"> Submit </button>
</form>
Below is the list of available methods.
Validate data with defined rules:
const { validate } = use('Validator')
const validation = await validate(data, rules)
if (validation.fails()) {
return validation.messages()
}
You can optionally pass custom error messages to return when your validation fails as your third method parameter. |
Same as validate
but continues to validate all fields, whereas the validate
method stops on first error:
const { validateAll } = use('Validator')
const validation = await validateAll(data, rules)
This method returns a new object with sanitized data:
const { sanitize } = use('Validator')
const data = sanitize(request.all(), rules)
Returns a reference to Indicative’s sanitizor:
const { sanitizor } = use('Validator')
const slug = sanitizor.slug('My first blog post')
Returns a reference to Indicative’s formatters:
const { formatters } = use('Validator')
validate(data, rules, messages, formatters.JsonApi)
Data validation normally occurs during the HTTP request/response lifecycle, and you can end up writing the same validation code inside each controller.
AdonisJs Route Validators can make the repetetive process of validation simpler:
// For a single route
Route
.post('users', 'UserController.store')
.validator('StoreUser')
// For a resourceful route
Route
.resource('users', 'UserController')
.validator(new Map([
[['users.store'], ['StoreUser']],
[['users.update'], ['UpdateUser']]
]))
Validators live inside the app/Validators directory.
|
Let’s create a StoreUser
validator by using the adonis
command:
> adonis make:validator StoreUser
create: app/Validators/StoreUser.js
Now, all we need to do is define our rules on the validator:
'use strict'
class StoreUser {
get rules () {
return {
email: 'required|email|unique:users',
password: 'required'
}
}
}
module.exports = StoreUser
If validation fails, the validator automatically sets the errors as flash messages and redirects the user back to the form.
If the request has an Accept: application/json header, the response gets sent back as JSON.
|
Default error messages can be confusing for the end user so you might want to create your own custom validation error messages.
AdonisJs provides an effortless way to do this.
Simply declare a messages
method on your route validator and return an object with your messages per rule, like so:
'use strict'
class StoreUser {
get rules () {
return {
email: 'required|email|unique:users',
password: 'required'
}
}
get messages () {
return {
'email.required': 'You must provide a email address.',
'email.email': 'You must provide a valid email address.',
'email.unique': 'This email is already registered.',
'password.required': 'You must provide a password'
}
}
}
module.exports = StoreUser
To validate all fields, set validateAll
to true on the class prototype:
'use strict'
class StoreUser {
get validateAll () {
return true
}
}
module.exports = StoreUser
You can sanitize user input by defining sanitizationRules
, which are performed on request data before validation occurs:
class StoreUser {
get sanitizationRules () {
return {
email: 'normalize_email',
age: 'to_int'
}
}
}
module.exports = StoreUser
Since every application is structured differently, there are times when automatic failure handling may be undesirable.
You can manually handle failures by adding a fails
method to your validator:
class StoreUser {
async fails (errorMessages) {
return this.ctx.response.send(errorMessages)
}
}
module.exports = StoreUser
You may want to validate custom properties which are not part of the request body (for example, headers).
This can be done by defining a data
property on your validator class:
class StoreUser {
get rules () {
return {
sessionId: 'required'
}
}
get data () {
const requestBody = this.ctx.request.all()
const sessionId = this.ctx.request.header('X-Session-Id')
return Object.assign({}, requestBody, { sessionId })
}
}
module.exports = StoreUser
You can also define the Indicative formatter as a property on the validator class:
const { formatters } = use('Validator')
class StoreUser {
get formatter () {
return formatters.JsonApi
}
}
You may want to perform checks to ensure the user is authorized to take the desired action.
This can be done by defining an authorize
method on your validator class:
class StoreUser {
async authorize () {
if (!isAdmin) {
this.ctx.response.unauthorized('Not authorized')
return false
}
return true
}
}
module.exports = StoreUser
Return a boolean from the authorize method to tell the validator whether or not to forward the request to the controller.
|
All route validators can access the current request context via this.ctx
.
AdonisJs supports all Indicative validations, but also adds a few custom rules.
Below is the list of custom AdonisJs rules.
Ensures a given value is unique to a given database table:
'use strict'
class StoreUser {
get rules () {
return {
email: 'unique:users,email'
}
}
}
When updating an existing user profile, there is no point checking their email address when enforcing the unique
rule. This can be done by defining an ignoreField (id)
and ignoreValue (userId)
:
class StoreUser {
get rules () {
const userId = this.ctx.params.id
return {
email: `unique:users,email,id,${userId}`
}
}
}
As an example of how to extend the AdonisJs Validator
, let’s add a new rule to ensure a post exists when adding a new comment to the database.
We’ll call this rule exists
:
const Validator = use('Validator')
const Database = use('Database')
const existsFn = async (data, field, message, args, get) => {
const value = get(data, field)
if (!value) {
/**
* skip validation if value is not defined. `required` rule
* should take care of it.
*/
return
}
const [table, column] = args
const row = await Database.table(table).where(column, value).first()
if (!row) {
throw message
}
}
Validator.extend('exists', existsFn)
We can use our new exists
rule like so:
get rules () {
return {
post_id: 'exists:posts,id'
}
}
Since the code to extend Validator need only execute once, you could use providers or Ignitor hooks to do so. Read Extending the Core for more information.
|