Why API validation is a contract An API client needs to know which input failed, why it failed, and whether resubmitting the same request could succeed. A...
Why API validation is a contract
An API client needs to know which input failed, why it failed, and whether resubmitting the same request could succeed. A generic 500 response for invalid input forces the mobile or frontend team to guess. Laravel's validation facilities provide a predictable 422 response for requests expecting JSON, including a message and field keyed errors. The useful work is defining rules that reflect the business operation and keeping that contract stable.
Consider an endpoint that creates a project quote. A name and email might be required, while a budget range could be optional. Validation should reject unsupported ranges before persistence. It should not trust a user supplied status such as "approved"; that field belongs to the server.
Put rules close to the request
For a nontrivial endpoint, a Form Request keeps validation and authorization out of the controller's main path. The following rules are illustrative:
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:120'],
'email' => ['required', 'email', 'max:255'],
'budget' => ['nullable', Rule::in(['small', 'medium', 'large'])],
'message' => ['required', 'string', 'max:5000'],
];
}
Import Rule from Illuminate\Validation\Rule. After validation, create the record from the validated array, not the complete request payload. If the model includes internal attributes, explicit field mapping is easier to audit than passing all input to mass assignment.
Choose one error format
Laravel returns a structured error body when the client sends an Accept: application/json header. Agree on this header with mobile and browser clients. A field can have multiple errors, so treat each errors[field] value as an array. The UI may display the first message, but logging and tests should preserve the full response.
Business conflicts are different from invalid fields. A duplicate idempotency key or a quote that can no longer be edited may deserve a 409 response with a stable application error code. Document those codes separately from 422 field errors. Avoid returning exception class names or database statements to clients.
Validate nested and conditional data
Arrays need both shape limits and item rules. For example, services can be an array of at most ten IDs, and each services.* value must exist in the allowed set. Conditional fields should use explicit rules so the client knows when they are required. Check ownership in the authorization layer rather than relying on an exists rule that reveals another tenant's record.
For files, validate uploaded media as files with allowed types and size limits. Never accept a filename or public path as proof that a file has been safely stored.
Test the consumer experience
Send a valid request, a missing required field, several simultaneous field errors, an unexpected attribute, and a request from a user without permission. Assert the status, JSON shape, and absence of saved data after failure. Test with the same headers and authentication method used by the real client; a browser form test can take a different validation path from a JSON API request.
Troubleshooting
If a client receives an HTML redirect instead of JSON, inspect its Accept header and route middleware. If a field appears to validate but is discarded, check the Form Request rules and the controller's mapping. If nested item errors are hard to display, keep the dotted field keys and map them to array positions in the client rather than flattening them into one vague message.
Conclusion
A reliable validation contract makes errors actionable without leaking internals. Define allowed input precisely, distinguish 422 from business conflicts, and test what the client actually receives. For the surrounding application architecture, see web and SaaS development.
References