Here is a small surprise in Laravel validation. The integer rule passes the string "10". For a form that is fine, because browsers send everything as text. For a JSON API it is a quiet contract break: your docs say age is an integer, a client sends "age": "10", and you accept it. Laravel has a one-word fix, integer:strict. I ran both rules against a table of values on Laravel 13.35.0 so you can see exactly what changes, including a few results I did not expect.
The tip in one line
$request->validate([
'age' => ['required', 'integer:strict'],
]);
The Laravel documentation describes the strict parameter as considering the field valid only if its type is integer, so strings with integer values are invalid. It also notes that plain integer does not check the PHP variable type, only that the input is accepted by PHP's FILTER_VALIDATE_INT. The framework source shows the difference clearly: strict mode is is_int($value), and normal mode is filter_var($value, FILTER_VALIDATE_INT) !== false.
What actually passes
I ran each value through the real validator. Here are the results, with 10.0 meaning a float and "10" a string:
| Value | integer | integer:strict |
|---|---|---|
10 (int) |
pass | pass |
0 (int) |
pass | pass |
"10" |
pass | fail |
10.0 (float) |
pass | fail |
true |
pass | fail |
" 10" (leading space) |
pass | fail |
"+10" |
pass | fail |
"10.0" |
fail | fail |
"010" |
fail | fail |
1.5 |
fail | fail |
"abc", false, null
|
fail | fail |
The tip is right: with integer:strict, "10", 10.0 and true all fail, and only a real int passes. The surprise is on the left side. Plain integer accepts true, and also strings with a leading space or plus sign. So a client sending {"quantity": true} gets through the normal rule. That is exactly the kind of input that ends up as 1 in your database.
Where strict mode works, and where it breaks
This is the part that can bite you. Strict mode checks the PHP type of the value, so it depends on how the data arrived:
-
JSON request body: types survive. Decoding
{"age":10,"age_s":"10","age_f":10.0,"age_b":true}gave an int, a string, a float and a bool, so strict validation can tell them apart. This is the case it is built for. -
HTML forms and query strings (
?page=2): every value is text.integer:strictwould reject all of them, including a perfectly normal2. Use plainintegerthere.
If one endpoint serves both JSON clients and form posts, do not apply strict mode to it blindly. Test it with the real kind of requests your clients send.
The same idea for numeric and boolean
The documentation shows a strict parameter for two related rules, and I tested both:
-
numeric:strictrejects numeric strings. In my run"10"failed, while10,10.0and1.5passed. Use it for prices and amounts where you want a JSON number, not text. -
boolean:strictaccepts only realtrueandfalse. Plainbooleanalso accepted1,0,"1"and"0", and all four failed in strict mode. Note that"true"as a string fails both versions.
$request->validate([
'quantity' => ['required', 'integer:strict', 'min:1'],
'price' => ['required', 'numeric:strict'],
'gift_wrap' => ['required', 'boolean:strict'],
]);
Missing and null values
Strict mode does not change how required and nullable work. With required|integer:strict, a missing field failed. A null value failed unless the rule list included nullable, and with nullable|integer:strict it passed. Add nullable only on fields that are really optional.
Should you use it?
For a public JSON API with documented types, yes. It gives clients a clear error the first time they send the wrong type, instead of letting sloppy data in and fixing it later. The honest trade-off: stricter rules mean some existing clients that send "10" will start getting 422 errors. If that is a risk, roll it out on new endpoints first, or version the API.
References
Originally published on DEV Talk.
Top comments (0)