neofusion/json-rpc-bundle
NeoFusion JSON-RPC Bundle provides a JSON-RPC 2.0 server and client for Symfony applications, letting you expose services as remote procedures and call them from other apps. It handles request parsing, method resolution, error responses, and batch calls, keeping your API layer clean and spec-complia...
This is a Symfony bundle, not a Laravel package — there is no service provider, no config/json_rpc.php, and no facade. As a Laravel developer you have two realistic entry points: (a) use it inside a Symfony app that your team also owns, or (b) read it as a reference implementation and port the ~300 lines of dispatch logic into a Laravel controller + container binding. Given it is archived (last release 2019-05-28) and has essentially zero dependents, treat it as a spec/implementation reference first and a dependency second.
Minimal steps if you are actually installing it in Symfony:
composer require neofusion/json-rpc-bundle:^1.0 (the archived release will not tolerate loose constraints against modern Symfony; expect to downgrade Symfony or fork).config/bundles.php (NeoFusion\JsonRpcBundle\NeoFusionJsonRpcBundle::class => ['all' => true]).DependencyInjection/Configuration.php for the exact root key and endpoint option rather than trusting the README, which is thin.json_rpc.method tag with a method attribute naming the exposed namespace, e.g. math).POST a JSON-RPC 2.0 envelope to the endpoint:{"jsonrpc":"2.0","method":"math.add","params":{"a":1,"b":2},"id":1}
Where to look first: Controller/ (the HTTP entrypoint and envelope validation), Service/ (the dispatcher that resolves method → tagged service → public method), and DependencyInjection/ (tag name, config schema). The README is short; the dispatcher is the actual contract.
First use case: expose a handful of internal application services (pricing, inventory lookups, reporting) over an internal-only RPC endpoint that a separate frontend or mobile BFF calls, instead of hand-rolling one controller per operation.
Tag-per-class, method-per-public-function. The idiomatic pattern is a small class per domain whose public methods are the RPC methods. The tag's method attribute is the namespace prefix; the public method name is the suffix. Keep these classes thin — they should delegate to real domain services, not contain business logic, because everything public is effectively API surface.
// config/services.yaml
App\Rpc\MathRpc:
tags:
- { name: json_rpc.method, method: 'math' }
Named parameters are the contract. Params arrive as an associative array and are bound by name. Always call with named params ("params":{"a":1,"b":2}), never positional arrays, because the dispatcher maps them to method arguments — positional payloads break the moment you reorder a signature. Document the parameter shape per method; there is no schema validation in the bundle.
One endpoint, many methods. Don't create multiple routes for multiple operations. The whole point is a single /json-rpc endpoint with method-based routing — that is what makes batch requests meaningful. A batch is just a JSON array of envelopes; use it to coalesce dashboard/mobile startup calls into one round-trip.
Auth and rate limiting via Symfony listeners, not the bundle. There is no built-in authentication. Mount the endpoint behind your existing firewall, and use a kernel.request listener or a security voter to inspect the method name and enforce per-method authorization. This is the cleanest extension point the bundle leaves you.
Error mapping through exceptions. Throw a domain exception from a tagged method and let a listener (or a small wrapper) translate it into a JSON-RPC error object with a stable code and message. Reserve the transport-level codes (-32700 parse, -32600 invalid request, -32601 method not found, -32602 invalid params) for the bundle and use a private -32000..-32099 range for application errors. Clients will branch on code, so define the codes once in a shared enum.
Laravel port pattern (if you are not using Symfony). The bundle's value to a Laravel team is the shape, not the code. Bind a dispatcher singleton, key a registry off a config array ('methods' => ['math.add' => [MathRpc::class, 'add']]), validate the envelope (version, method string, params array-or-object, id scalar-or-absent for notifications), and return a single controller response that handles both single and batch payloads. That is the entire bundle, and it is ~150 lines you control.
Testing. Hit the endpoint with Symfony's KernelBrowser (or Laravel's $this->postJson) and assert on the jsonrpc, result/error, and id envelope fields — never on raw response bodies. Add one test per error code; the dispatcher's envelope handling is the part most likely to drift if you fork.
composer.lock for transitive Symfony constraints before adopting.composer require it into Laravel will pull Symfony HTTP/DI components and give you nothing you can call. Port it or wrap it behind an HTTP boundary.public function on a tagged class becomes an RPC method. Keep RPC classes free of helpers, __construct dependencies aside, and prefer a facade class that explicitly forwards to a few methods over a fat service.null for optional fields.id is a notification and must produce an empty response body (HTTP 204 or empty 200 depending on your setup). Clients that always expect a JSON body will choke — document this.Configuration.php, not the README. Grep for endpoint in DependencyInjection/ before assuming a default.kernel.request/kernel.response listener — the bundle's own logging is sparse, and a malformed envelope gives a generic -32600 with no hint. Symfony profiler will show the controller but not the dispatch decision.kernel.exception (error mapping), and the dispatcher service if you fork (method registry). Everything else is hardcoded and not worth fighting.How can I help you explore Laravel packages today?