Laravel Tutorial · Requests and Responses · Lesson 9 of 27
Laravel Validation
Validate input with rules like required, email, min and unique; get automatic 422 JSON errors; and move complex rules into Form Request classes.
Validating a request
$request->validate([...]) checks the input against rules and returns only the validated data. If validation fails:
- for JSON/API requests, Laravel answers 422 with an
errorsobject listing every failed field, - for HTML forms, it redirects back with the errors and old input (shown with
@errorin Blade).
Rules can be strings ('required|email|max:255') or arrays. Common ones: required, nullable, string, integer, numeric, boolean, email, url, min, max, between, in:a,b, confirmed, date, after:today, unique:users,email, exists:categories,id, array, and items.*.qty for each element of an array.
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/signup', function (Request $request) {
$data = $request->validate([
'name' => 'required|string|min:2|max:50',
'email' => 'required|email',
'age' => 'nullable|integer|min:13',
'plan' => 'required|in:free,pro',
'password' => 'required|min:8|confirmed', // needs a matching password_confirmation field
]);
return response()->json(['welcome' => $data['name'], 'fields' => array_keys($data)], 201);
});
visit('POST', '/signup', ['name' => 'Ana', 'email' => '[email protected]', 'plan' => 'pro',
'password' => 'correct-horse', 'password_confirmation' => 'correct-horse', 'is_admin' => true]);
visit('POST', '/signup', ['name' => 'A', 'email' => 'not-an-email', 'age' => 9, 'plan' => 'gold',
'password' => 'short', 'password_confirmation' => 'different']);
Output
POST /signup -> 201 {"welcome":"Ana","fields":["name","email","plan","password"]}
POST /signup -> 422 {"message":"The name field must be at least 2 characters. (and 5 more errors)","errors":{"name":["The name field must be at least 2 characters."],"email":["The email field must be a valid email address."],"age":["The age field must be at least 13."],"plan":["The selected plan is invalid."],"password":["The password field must be at least 8 characters.","The password field confirmation does not match."]}}Run this on your computer — see “Get Started” for setup.
Form Request classes
For bigger forms, create a Form Request (php artisan make:request StoreUserRequest) with rules(), optional messages() and authorize(). Type-hint it in the controller and validation runs before your method is called.
<?php
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Route;
class StoreOrderRequest extends FormRequest
{
public function authorize(): bool
{
return true; // e.g. return $this->user()->can('order');
}
public function rules(): array
{
return [
'items' => 'required|array|min:1',
'items.*.sku' => 'required|string',
'items.*.qty' => 'required|integer|between:1,10',
'note' => 'nullable|string|max:200',
];
}
public function messages(): array
{
return ['items.*.qty.between' => 'Each quantity must be between 1 and 10.'];
}
}
Route::post('/orders', fn (StoreOrderRequest $request) => ['accepted' => count($request->validated('items'))]);
visit('POST', '/orders', ['items' => [['sku' => 'MUG', 'qty' => 2], ['sku' => 'TEA', 'qty' => 1]]]);
visit('POST', '/orders', ['items' => [['sku' => 'MUG', 'qty' => 50]]]);
visit('POST', '/orders', []);
Output
POST /orders -> 200 {"accepted":2}
POST /orders -> 422 {"message":"Each quantity must be between 1 and 10.","errors":{"items.0.qty":["Each quantity must be between 1 and 10."]}}
POST /orders -> 422 {"message":"The items field is required.","errors":{"items":["The items field is required."]}}Run this on your computer — see “Get Started” for setup.
Test yourself
What does $request->validate() return when validation passes?
trueThe whole requestOnly the validated fieldsA Validator object
Show answer
Answer: Only the validated fields