yoanm/symfony-jsonrpc-params-validator
A Symfony bundle that validates JSON-RPC request parameters using the Symfony Validator component. It plugs constraint-based validation into JSON-RPC handlers, helping you enforce typed rules and return clean errors for invalid calls.
First, a scoping note: this is a Symfony bundle, not a Laravel package. As of v3.0.2 its composer.json type is symfony-bundle, which exists purely so Symfony Flex can auto-register it. Nothing about that flag makes it installable, discoverable, or useful as a bundle inside a Laravel app — Laravel's package discovery ignores type entirely.
So the practical entry point for a Laravel developer is the underlying library, yoanm/jsonrpc-params-validator, which is framework-agnostic and only needs symfony/validator (which coexists fine with Laravel's own Validator).
Minimal steps:
composer require yoanm/jsonrpc-params-validator symfony/validator
Then look in this order:
ParamsValidatorInterface — the one method you actually call.Constraint).InvalidParamsException — the failure contract you must translate to JSON-RPC -32602.First use case: you expose a JSON-RPC-ish endpoint (or an internal command bus that mimics one) and want per-method parameter contracts declared once, validated before your handler runs.
// Illustrative shape — check the exact constructor for your installed version
$validator = new ParamsValidator(
Validation::createValidator(),
[
'user.create' => new Collection([
'email' => [new NotBlank(), new Email()],
'password' => [new NotBlank(), new Length(['min' => 12])],
]),
]
);
$params = $validator->validate('user.create', $request->input('params', []));
If you are only building a Laravel app, stop here and use $request->validate() — you gain little from a second validation stack.
Wrap it in a single service, not in controllers. Bind the validator interface in a service provider and inject it into whatever dispatches your RPC methods, so the constraint map lives in one config file rather than being rebuilt per call.
// config/jsonrpc.php
return [
'methods' => [
'user.create' => [
'email' => ['required', 'email'],
'password' => ['required', 'min:12'],
],
],
];
Then map that config into Symfony Constraint objects once at container build time (a small factory is enough) — rebuilding constraints per request is the usual performance trap.
Normalise params before validating. JSON-RPC allows positional params ([1, "a"]) and named params ({"id": 1}). The validator expects one canonical shape; convert positional arrays to associative using your method's declared argument order before handing them over. Doing this inside a middleware keeps handlers clean.
Translate exceptions at the boundary. InvalidParamsException carries violations, not an HTTP status. Convert to a JSON-RPC error envelope with code -32602 and put the violation property paths in data — do not let it bubble up as a Laravel 422, which will confuse RPC clients expecting a -32xxx code.
Use it as a decorator, not a base class. Wrap each handler:
final class ValidatingHandler
{
public function __construct(
private ParamsValidatorInterface $validator,
private Handler $inner,
private string $method,
) {}
public function __invoke(array $params): mixed
{
return ($this->inner)($this->validator->validate($this->method, $params));
}
}
This keeps the handler testable without the validator and lets you reuse the same constraint map for an HTTP controller and a queue job.
Reuse constraints for OpenAPI/docs generation. Because constraints are objects, you can walk the map and emit JSON Schema for your RPC methods — one source of truth for validation and documentation.
type: library → type: symfony-bundle alters zero runtime behaviour. If you are pinned to v3.0.1 and skip this release, you lose nothing functionally.symfony-bundle type means Flex will now offer to register the bundle in the Symfony side on composer update. Harmless, but it can surprise CI if you assert on bundles.php contents.type is also read by deploy tooling. Some private Composer plugins and artifact builders branch on type (library vs symfony-bundle). If you vendor your composer.lock into a build image, re-run the build after upgrading rather than assuming a no-op.Collection/All/Required nesting. A top-level Collection does not recurse into arbitrary-depth arrays; deeply nested payloads are where most "validation passed but handler crashed" bugs originate.lang/ files. Map them yourself if the RPC surface is public.violation->getPropertyPath() alongside the message — the path is what tells you which nested param failed, and it is what RPC clients need to render field-level errors.Constraint + ConstraintValidator for domain rules (e.g. "IBAN belongs to a supported country"), and keep the constraint map free of closures so it stays serialisable for caching.How can I help you explore Laravel packages today?