Validation

  • BEAR.Sunday validates with JSON Schema.
  • For validation with web forms, see Forms.

Validation with JSON Schema

Overview

You can define and validate the input and output specifications of resource APIs with JSON Schema. This lets you manage the API specification in a form that both humans and machines understand, and also output API documentation as ApiDoc.

Setup

Module configuration

Configure one of the following depending on where validation should apply:

  • Validate in all environments: configure in AppModule
  • Validate only in the development environment: configure in DevModule
use BEAR\Resource\Module\JsonSchemaModule;
use BEAR\Package\AbstractAppModule;

class AppModule extends AbstractAppModule
{
    protected function configure(): void
    {
        $this->install(
            new JsonSchemaModule(
                $appDir . '/var/json_schema',  // for schema definitions
                $appDir . '/var/json_validate' // for validation
            )
        );
    }
}

2. Create the required directories

mkdir -p var/json_schema
mkdir -p var/json_validate

Basic usage

1. Define a resource class

use BEAR\Resource\Annotation\JsonSchema;

class User extends ResourceObject
{
    #[JsonSchema('user.json')]
    public function onGet(): static
    {
        $this->body = [
            'firstName' => 'mucha',
            'lastName' => 'alfons',
            'age' => 12
        ];
        return $this;
    }
}

2. Define the JSON Schema

var/json_schema/user.json:

{
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string",
            "maxLength": 30,
            "pattern": "[a-z\\d~+-]+"
        },
        "lastName": {
            "type": "string",
            "maxLength": 30,
            "pattern": "[a-z\\d~+-]+"
        }
    },
    "required": ["firstName", "lastName"]
}

Advanced usage

Specifying an index key

When the response body has an index key, specify it with the key parameter:

class User extends ResourceObject
{
    #[JsonSchema(key: 'user', schema: 'user.json')]
    public function onGet(): static
    {
        $this->body = [
            'user' => [
                'firstName' => 'mucha',
                'lastName' => 'alfons',
                'age' => 12
            ]
        ];

        return $this;
    }
}

Validating arguments

To validate method arguments, specify a schema with the params parameter:

class Todo extends ResourceObject
{
    #[JsonSchema(
        key: 'user',
        schema: 'user.json',
        params: 'todo.post.json'
    )]
    public function onPost(string $title)
    {
        // method implementation
    }
}

var/json_validate/todo.post.json:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "/todo POST request validation",
    "type": "object",
    "properties": {
        "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
        }
    },
    "required": ["title"]
}

target

To apply schema validation to the representation of the resource object (the rendered result) rather than to the body of the ResourceObject, specify the target='view' option. In HAL format, this allows you to describe a schema for _link.

#[JsonSchema(schema: 'user.json', target: 'view')]

Schema authoring tools

The following tools are useful for creating JSON Schemas:

Exception Handling

When JSON Schema validation fails, BEAR.Resource (1.33.0+) throws a typed exception that tells you whether the problem was in the request (client error) or the response (server bug):

Exception HTTP Meaning
JsonSchemaRequestException 400 Input params failed schema validation
JsonSchemaResponseException 500 Response body failed schema validation

Both extend JsonSchemaException, so you can catch either specifically or together.

Accessing Structured Errors

$e->getErrors() returns a JsonSchemaErrors collection:

use BEAR\Resource\Exception\JsonSchemaRequestException;
use BEAR\Resource\Exception\JsonSchemaResponseException;

try {
    $response = $resource->post('app://self/user', $params);
} catch (JsonSchemaRequestException $e) {
    $errors = $e->getErrors();        // JsonSchemaErrors
    $errors->count();                 // number of violations
    $errors->first();                 // first JsonSchemaError or null

    // Iterate over each error
    foreach ($errors as $error) {
        echo $error->property;        // e.g. "email"
        echo $error->message;         // human-readable message
    }

    // Group by field — useful for building API error responses
    $byField = $errors->byProperty(); // array<string, list<JsonSchemaError>>

    // Bulk format with a template
    $text = $errors->format('{property}: {message}\n');
} catch (JsonSchemaResponseException $e) {
    // This is a server-side bug: the resource returned data that
    // does not match its own declared schema.
    error_log((string) $e);
}

Default Framework Behaviour

BEAR.Sunday’s default ThrowableHandler (1.x) already maps JsonSchemaRequestException to a 400 application/vnd.error+json response. No extra setup is needed for API (JSON) contexts.

JSON vs HTML Contexts

The appropriate response format depends on context:

  • API / JSON context — return a JSON error body (e.g. {"code": 400, "message": "..."})
  • HTML context — render an HTML error page for browser users

Production apps typically override the default handler for each context. See BEAR.Examples for a reference implementation using AppThrowableHandler (JSON) and HtmlThrowableHandler (HTML), backed by a shared ExceptionStatusMapper.