tourze/json-rpc-http-endpoint-bundle
A Symfony bundle that exposes JSON-RPC methods over HTTP endpoints, simplifying request routing and dispatch for PHP services. Useful for teams standardizing API access around the JSON-RPC protocol with minimal configuration and clean integration into existing Symfony applications.
tourze/json-rpc-http-endpoint-bundle gives you a JSON-RPC 2.0 endpoint over HTTP — a single URI that accepts POST bodies with method, params, and id, and returns spec-shaped result/error envelopes. Note the -bundle suffix: this is a Symfony bundle by naming convention. Before you build anything, confirm Laravel support by checking composer.json for illuminate/* requirements or a service provider. If it only ships a Symfony Bundle class, you'll be wiring the underlying classes into Laravel's container yourself rather than php artisan vendor:publish-ing your way to a working endpoint.
Minimal path:
composer require tourze/json-rpc-http-endpoint-bundle
Then look at, in order:
composer.json require/autoload block — this tells you immediately whether Laravel is a first-class target or a Symfony-only package you're bending.-32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal). You need to know how app exceptions are translated.First use case: a read-only method. Register something like user.get, point it at an existing service or action class, and hit it:
curl -X POST https://app.test/rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"user.get","params":{"id":1},"id":1}'
Getting one method round-tripping end-to-end before you touch auth, batching, or error mapping will surface container-wiring problems early.
Wrap your existing actions, don't rewrite them. The healthiest pattern is a thin RPC layer that delegates to service classes or invokable actions you already have. Keep RPC methods as adapters: validate params, call the domain service, shape the result. Resist putting business logic in the method handler — it's the least testable place in the stack.
Register methods at the boundary, not scattered. Whether the package uses a config map, an attribute/tag system, or a registerMethod() call, centralize it. A single config/rpc.php returning ['methods' => ['user.get' => UserGetMethod::class]] makes it trivial to audit your exposed surface area — which is exactly what you want when someone asks "what can the outside world call?"
Map params explicitly. JSON-RPC allows params as an array (positional) or an object (named). Pick one convention — named is almost always the right call for maintainability — and validate it. Don't splat $params into a constructor; that's how a typo becomes a 500 instead of a -32602.
Reuse Laravel middleware for auth, not RPC internals. Authenticate at the route/middleware layer (auth:sanctum, API key middleware, rate limiting) before the payload is ever parsed. JSON-RPC's own error envelope is for protocol and application errors; a 401 from middleware is a transport-level concern and belongs there.
Bridge exceptions deliberately. Register a single translation point that catches your domain exceptions and converts them into JSON-RPC error objects with your own application error-code range (use positive integers above -32000 for server-defined errors — the spec reserves negatives). Unmapped exceptions should fall through to -32603 with a generic message, never a stack trace.
Batch handling. If batching is supported, apply per-request limits (e.g. 20 calls) and make sure rate limiting counts calls, not HTTP requests — otherwise one batched POST bypasses your throttle entirely.
Notifications are fire-and-forget. Requests with no id (not "id": null on a request that expects a response) must return an empty body, 204, or nothing at all — not a result envelope. Test this explicitly; it's the most commonly broken part of JSON-RPC implementations.
Testing. Because everything is a POST to one URL, your feature tests are uniform: build a payload, postJson('/rpc', ...), assert on json('result') or json('error.code'). Write a small helper so method tests are one-liners; you'll have dozens.
1.0.0 is a stability commitment, not a feature list. The only release signal here is 0.1.4 → 1.0.0. Semver says the public API is now frozen and that pre-1.0 minor bumps (which are breaking by convention) are over. Practically: if you had pinned ^0.1, 1.0.0 will not resolve — expect to bump the constraint and then fix whatever 0.1.x API drifted. Treat the jump as likely breaking until the changelog proves otherwise, and read the compare diff before upgrading a production service.
The changelog link is empty of signal. compare/0.1.4...1.0.0 as the entire release note means there's no migration guide, no deprecation list, and no summary of behavioral changes. For a 1.0.0 that's a red flag for upgrade risk. Read the actual diff, and pin exactly (1.0.0) rather than floating (^1.0) until you've verified the surface.
Bundle vs. provider. If there's no Laravel service provider, you'll hand-register the endpoint in routes/api.php with a closure or a thin controller that resolves the package's handler from the container. Budget time for this; it's usually a half-day of glue, not a five-minute install.
Always HTTP 200. Well-behaved JSON-RPC returns 200 OK even for errors, with the error in the body. This breaks Laravel conventions — assertStatus(422) won't work, HTTP-level monitoring won't see failures, and Horizon/Telescope dashboards will look healthy while everything is on fire. Add body-level error-rate logging or you'll have a blind spot.
Content-Type strictness. Many implementations require exactly application/json and reject application/json; charset=utf-8 or missing headers. postJson() is fine; hand-rolled curl in scripts and webhook senders often isn't. Confirm behavior and decide whether to relax it.
params shape mismatches. Positional vs. named params, and params omission vs. "params": [] vs "params": {} are three distinct inputs. Test all three against each method — different implementations treat them differently.
Error-code collisions. Don't invent application error codes in the -32000..-32099 server-error range if the package already uses it for internal failures. Allocate your own range and document it.
Log correlation. JSON-RPC has no request path to distinguish calls in logs — every entry looks like POST /rpc. Include the RPC method name and the request id in your log context (or a generated correlation ID when id is absent), or debugging production will be miserable.
Extension points worth knowing: the method resolver (swap in a container-aware resolver), the error translator (map your exception hierarchy), and the serializer (if your result shaping differs from the default). Those three cover nearly every customization you'll need; avoid forking the request-handling core.
How can I help you explore Laravel packages today?