Securing AdonisJS v6 Request Data with VineJS Validators
Learn how to implement VineJS validators in AdonisJS v6 to prevent mass-assignment vulnerabilities and ensure type safety between HTTP requests and database models.
13 Apr 2026, 04:56 UTC

Preventing Mass-Assignment and Type Errors
Passing raw request data directly into a database model is a security risk known as mass-assignment. If a user sends an unexpected field—such as is_admin: true—and your model accepts all input, they can escalate their own privileges. The solution in AdonisJS v6 is to use VineJS to create a strict filter between the HTTP request and your business logic.
The primary takeaway is to never use request.body() for database operations. Instead, use request.validateUsing(). This ensures that only defined fields are permitted, types are coerced correctly (e.g., a string "123" becomes a number 123), and the request is rejected with a 422 Unprocessable Entity status before it ever touches your controller logic.
Defining a Reusable Validator
In AdonisJS v6, validators are compiled schemas. Compiling them once at the module level improves performance because VineJS does not have to re-parse the schema on every request.
Create a validator file (e.g., app/validators/post.ts):
import vine from '@vinejs/vine'
export const createPostValidator = vine.compile(
vine.object({
title: vine.string().trim().minLength(3).maxLength(255),
body: vine.string().minLength(10),
category_id: vine.number().positive(),
})
)
Wiring the Validator into a Controller
The request.validateUsing() method is the bridge between the incoming data and your validator. It is an asynchronous operation that returns a typed object containing only the validated data.
Run this logic within your controller method (requires @adonisjs/core):
import { HttpContext } from '@adonisjs/core/http'
import { createPostValidator } from '#validators/post'
import Post from '#models/post'
export default class PostsController {
async store({ request, response }: HttpContext) {
// 1. Validate the request. This throws a ValidationException if it fails.
const payload = await request.validateUsing(createPostValidator)
// 2. Use the 'payload' variable, NOT 'request.body()'
// This prevents mass-assignment of fields not in the validator
const post = await Post.create(payload)
return response.created(post)
}
}
Handling Validation Failures
You do not need to wrap validateUsing() in a try-catch block for standard API responses. AdonisJS includes a global exception handler that catches ValidationException and automatically transforms it into a structured JSON response:
- Status Code: 422 Unprocessable Entity.
- Body: A JSON object mapping field names to specific error messages (e.g.,
{ "title": { "_messages": ["The title field is required"] } }).
Engineering Constraints and Common Pitfalls
Validation vs. Authorization
A common mistake is attempting to check ownership or permissions inside a VineJS schema. Validators check form (is this an email? is this a number?); they do not check permission (does this user own this post?). Use the AdonisJS Bouncer for authorization after the data has been validated.
The "Extra Fields" Trap
VineJS strips any fields provided in the request that are not explicitly defined in the vine.object() schema. This is a security feature. If you find that fields are "disappearing" before they reach your model, check that they are defined in your validator.
Version Mismatch
AdonisJS v6 uses VineJS. If you are following tutorials for AdonisJS v5, you will see schema.create() and request.validate(). These are deprecated and incompatible with v6. Ensure your package.json includes @vinejs/vine.
Verification Checklist
| Check | Action | Expected Result |
|---|---|---|
| Invalid Data | POST a request with a title shorter than 3 chars | 422 Response with error message |
| Mass-Assignment | POST a request including is_admin: true |
Field is stripped; model is created without is_admin |
| Type Coercion | POST category_id as a string "5" |
payload.category_id is the number 5 |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.