Validation in JSON:API
This guide explains how validation works in the Laravel Doctrine JSON:API package and how it integrates with Scribe documentation.
Introduction
Validation is a crucial part of any API. In JSON:API, validation ensures that incoming requests conform to both the JSON:API specification and your application's business rules. This package leverages Laravel's validation system to validate incoming requests and generate appropriate documentation.
Validation Rules
Setting Up Validation Rules
Validation rules for your JSON:API endpoints are defined in your controller or request classes. The package uses these rules to:
- Validate incoming requests
- Generate documentation examples for request bodies
- Document validation errors
Example of defining validation rules in a request class:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateUserRequest extends FormRequest
{
public function rules()
{
return [
'data.type' => 'required|string|in:users',
'data.attributes.name' => 'required|string|max:255',
'data.attributes.email' => 'required|email|unique:users,email',
'data.attributes.password' => 'required|string|min:8',
'data.relationships.roles.data' => 'sometimes|array',
'data.relationships.roles.data.*.type' => 'required_with:data.relationships.roles.data|string|in:roles',
'data.relationships.roles.data.*.id' => 'required_with:data.relationships.roles.data.*.type|string|exists:roles,id',
];
}
}
JSON:API Specific Validation
The package includes validation rules specific to JSON:API:
- Type validation: Ensures the resource type matches the expected type
- ID validation: Validates resource IDs
- Relationship validation: Validates relationship structure and linkage
- Attribute validation: Ensures attributes conform to expected formats
Integration with Scribe
How Validation Rules Generate Documentation
Scribe uses your validation rules to generate documentation in several ways:
- Request Body Examples: Scribe analyzes your validation rules to generate example request bodies that would pass validation
- Required Fields: Fields marked as required in your validation rules are clearly indicated in the documentation
- Field Types: Validation rules like
integer
,string
,boolean
help Scribe determine the correct data types for fields - Validation Constraints: Rules like
max:255
,email
,in:option1,option2
are documented as constraints on the fields - Error Responses: Scribe generates example error responses based on validation failures
Example Documentation Flow
When Scribe generates documentation for an endpoint:
- It identifies the controller method and any associated form request classes
- It extracts validation rules from these classes
- It generates example request bodies that would pass validation
- It documents the structure of validation error responses
JSON:API Validation Errors
When validation fails, the package returns a JSON:API compliant error response:
{
"errors": [
{
"status": "422",
"title": "Validation Error",
"detail": "The name field is required.",
"source": {
"pointer": "/data/attributes/name"
}
},
{
"status": "422",
"title": "Validation Error",
"detail": "The email must be a valid email address.",
"source": {
"pointer": "/data/attributes/email"
}
}
]
}
Best Practices
- Use Form Request Classes: Separate validation logic into dedicated form request classes for cleaner code
- Follow JSON:API Paths: Use dot notation to validate nested JSON:API structures (
data.attributes.name
) - Document Custom Rules: If you have custom validation rules, make sure to document them
- Include Examples: Provide examples of valid request bodies in your controller method docblocks
- Validate Relationships: Don't forget to validate relationship structures and linkage
Example Validation Rules for Common Scenarios
Creating a Resource
[
'data.type' => 'required|string|in:articles',
'data.attributes.title' => 'required|string|max:255',
'data.attributes.content' => 'required|string',
'data.relationships.author.data.type' => 'required|string|in:users',
'data.relationships.author.data.id' => 'required|string|exists:users,id',
]
Updating a Resource
[
'data.id' => 'required|string|exists:articles,id',
'data.type' => 'required|string|in:articles',
'data.attributes.title' => 'sometimes|string|max:255',
'data.attributes.content' => 'sometimes|string',
]
Updating a Relationship
[
'data.*.type' => 'required|string|in:tags',
'data.*.id' => 'required|string|exists:tags,id',
]