wa72/jsonrpc-bundle
A Symfony bundle for building JSON-RPC 2.0 servers and clients. It registers server endpoints via controllers, exposes services as RPC methods, and provides a client for calling remote JSON-RPC APIs, with support for batch requests, notifications, and error handling.
wa72/jsonrpc-bundle is a Symfony bundle, not a Laravel package — there is no service provider, facade, or config publish step you'd recognize. First decision: if you're actually in a Laravel codebase, this package won't install cleanly into your app and you should treat it as a reference implementation (see Gotchas). If you're in a Symfony app, the path is short:
composer require wa72/jsonrpc-bundle
config/bundles.php (Flex usually does this automatically): Wa72\JsonRpcBundle\Wa72JsonRpcBundle::class => ['all' => true].config/routes.yaml and pick your endpoint path:
wa72_jsonrpc:
resource: "@Wa72JsonRpcBundle/Resources/config/routing.xml"
prefix: /rpc
(Some configs prefer wa72_jsonrpc: { path: /rpc } — check the README for your installed version, since path configuration has moved between releases.)wa72.jsonrpc.service with an alias attribute, or list it under wa72_jsonrpc.services in config/packages/wa72_jsonrpc.yaml.The first use case is almost always an internal/partner API: you have a Symfony service (a report generator, a pricing calculator, a sync job) and you want it callable over HTTP without writing a controller per method. Post a JSON-RPC 2.0 body to /rpc and it invokes the method. Start by reading the bundle's README and the JsonRpcController, then the underlying wa72/jsonrpc library — the bundle is thin, and the protocol behavior (batch, notifications, error objects) lives in the library.
Tagged service as the unit of exposure. The idiomatic pattern is a thin facade service per bounded context, tagged with an alias, rather than exposing your domain services directly:
services:
App\Rpc\BillingRpc:
tags:
- { name: wa72.jsonrpc.service, alias: billing, method_names: ['createInvoice', 'getInvoice'] }
The method_names allowlist matters — see Gotchas. Methods become billing.createInvoice, billing.getInvoice.
Batch requests for free. JSON-RPC 2.0 batching (an array body) is handled by the controller. If your clients do dashboard-style fan-out (five lookups on page load), use it instead of five round trips — but keep batches small and independent; one slow member blocks the whole response.
Notifications. A request with no id is a notification and returns an empty 204. Useful for fire-and-forget audit or webhook-style calls, but remember errors are silent — log inside the method if you need observability.
Auth via the firewall, not the bundle. Put the endpoint behind a Symfony firewall with a stateless authenticator (HTTP Basic, API token, or JWT). Avoid session-based form login for this route; JSON-RPC clients aren't browsers. If browser clients are involved, add CORS at the web-server or a listener level.
DTOs at the boundary. Services should accept scalars/arrays and return arrays or simple DTOs that serialize cleanly. This keeps the RPC surface decoupled from Doctrine entities and makes error handling predictable.
Error mapping. Throw exceptions with JSON-RPC codes. The library maps them to the error object; anything unmapped becomes an internal error. Define a small exception hierarchy (InvalidParams, NotFound, Unauthorized) with fixed codes and reuse it across services so clients can branch reliably.
For Laravel developers thinking of porting the idea: the pattern worth copying is "container-registered class → introspect public methods → dispatch by alias.method." In Laravel the closest off-the-shelf equivalents are sajya/server or a single RpcController with an explicit method map. The auto-exposure trick is convenient but dangerous in both frameworks — an explicit allowlist is the durable design.
method_names, every public method on a tagged class is callable, including anything inherited from a parent or a trait. Audit the class hierarchy and prefer explicit allowlists. This is the single most common security mistake with this style of package.bin/console cache:clear. In dev, the Symfony profiler will show the JSON-RPC request as a normal sub-request — check the X-Debug-Token if responses look truncated (the toolbar injects into HTML, which corrupts JSON; use curl or set the Accept: application/json header, or disable the toolbar for the route)..json() on the response will throw. Check status before parsing.DateTime — decide on ISO-8601 strings at the boundary.application/json. Sending application/x-www-form-urlencoded or missing the header yields confusing parse errors rather than a clean 415.wa72/jsonrpc directly and skip the bundle; it gives you the protocol without the container magic.curl -s -X POST http://localhost:8000/rpc -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"billing.getInvoice","params":{"id":42}}' should be your first call in any environment. If it fails, the problem is routing or the firewall 90% of the time, not the RPC layer.How can I help you explore Laravel packages today?