league/openapi-psr7-validator
Installation:
composer require league/openapi-psr7-validator
Basic Validation (Request):
use League\OpenAPIValidation\PSR7\ValidatorBuilder;
$validator = (new ValidatorBuilder())
->fromYamlFile(__DIR__.'/api.yaml')
->getServerRequestValidator();
$match = $validator->validate($request); // $request is PSR-7 ServerRequestInterface
First Use Case: Validate incoming API requests against your OpenAPI spec in a Laravel middleware or controller.
use League\OpenAPIValidation\PSR15\ValidationMiddlewareBuilder;
$middleware = (new ValidationMiddlewareBuilder())
->fromYamlFile(__DIR__.'/api.yaml')
->getValidationMiddleware();
$app->pipe($middleware); // Laravel's PSR-15 middleware support
use League\OpenAPIValidation\PSR7\OperationAddress;
public function store(Request $request)
{
$validator = app(ValidatorBuilder::class)
->fromYamlFile(__DIR__.'/api.yaml')
->getRoutedRequestValidator();
$address = new OperationAddress('/users', 'post');
$validator->validate($address, $request);
// Proceed with business logic
}
use League\OpenAPIValidation\PSR7\OperationAddress;
public function show(Request $request, User $user)
{
$response = new Response(200, [], json_encode($user));
$validator = app(ValidatorBuilder::class)
->fromYamlFile(__DIR__.'/api.yaml')
->getResponseValidator();
$address = new OperationAddress('/users/{id}', 'get');
$validator->validate($address, $response);
}
use League\OpenAPIValidation\PSR7\ValidatorBuilder;
use Symfony\Contracts\Cache\CacheInterface;
public function validator(CacheInterface $cache)
{
return (new ValidatorBuilder())
->fromYamlFile(__DIR__.'/api.yaml')
->setCache($cache, 3600) // 1 hour TTL
->getServerRequestValidator();
}
use League\OpenAPIValidation\Schema\TypeFormats\FormatsContainer;
FormatsContainer::registerFormat('string', 'custom', function($value) {
return preg_match('/^custom-.*$/', $value);
});
Missing Content-Type Header:
Always ensure requests/responses include Content-Type headers. Catch NoContentType exceptions.
Path/Operation Mismatch:
Use getRoutedRequestValidator() when you know the exact endpoint to avoid performance overhead.
Schema Caching:
Cache keys are auto-generated. Override with overrideCacheKey() if needed:
->setCache($cache, 3600)
->overrideCacheKey('api_v1')
Nested Validation Errors:
Use ValidationFailed exceptions to access detailed error paths:
try {
$validator->validate($address, $request);
} catch (ValidationFailed $e) {
$errors = $e->getErrors(); // Array of error details
}
Schema Validation: Validate your OpenAPI spec first using Swagger Editor to catch syntax errors early.
Middleware Debugging: Wrap middleware in a try-catch to log validation failures:
try {
$middleware->process($request, $handler);
} catch (ValidationFailed $e) {
Log::error('Validation failed', ['errors' => $e->getErrors()]);
throw new HttpException(400, 'Invalid request');
}
Performance: Reuse validators and schemas across requests. Avoid rebuilding validators in hot paths.
Custom Error Responses:
Extend ValidationFailed to add custom error formatting:
class CustomValidationFailed extends ValidationFailed {
public function toArray(): array {
return ['errors' => $this->getErrors()];
}
}
Schema Modification: Modify the OpenAPI schema before validation:
$schema = Reader::readFromYaml(file_get_contents('api.yaml'));
$schema->paths->addPath('/custom', new PathItem(...));
$validator = (new ValidatorBuilder())->fromSchema($schema)->getValidator();
Security Schemes:
Add custom security validation logic by extending SecurityValidator:
class CustomSecurityValidator extends SecurityValidator {
protected function validateSecurity($securityRequirements, $request) {
// Custom logic
}
}
How can I help you explore Laravel packages today?