Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Json Rpc Container Bundle Laravel Package

tourze/json-rpc-container-bundle

Symfony bundle that integrates a JSON-RPC container, helping wire PHP services into JSON-RPC endpoints with dependency injection and framework configuration for clean, container-managed RPC APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

First, verify what you actually installed. The -bundle suffix is a strong signal this is a Symfony bundle, not a Laravel package — check composer.json for "type": "symfony-bundle" and a require on symfony/http-kernel. If so, you will not get a Laravel service provider, auto-discovery, or vendor:publish; you'll be consuming the underlying JSON-RPC classes with your own wiring.

Minimal steps:

  1. composer require tourze/json-rpc-container-bundle:^2.0 (pin it — see Gotchas).
  2. Read vendor/tourze/json-rpc-container-bundle/ in this order: composer.json → *Bundle.php (shows which services are registered and how) → DependencyInjection/ (shows what's configurable) → src/ (the actual dispatcher/server classes you'll reuse).
  3. Find the entry point class — typically something like a Server, Handler, or Dispatcher that accepts a method map and a request payload. That class is the whole package; everything else is Symfony plumbing.
  4. Build one route:
// routes/api.php
Route::post('/rpc', RpcController::class)->middleware(['auth:sanctum', 'throttle:60,1']);
class RpcController
{
    public function __invoke(Request $request, RpcDispatcher $rpc)
    {
        $result = $rpc->handle($request->json()->all());
        return response()->json($result);
    }
}

First use case: expose one existing service method (user.get) as a JSON-RPC method and call it from a front-end via POST /api/rpc. Get that single round-trip working before wiring a method registry.

Implementation Patterns

Method registry in config, not in code. Map method names to [class, method] pairs so the handler classes stay plain and testable:

// config/rpc.php
return [
    'methods' => [
        'user.get'    => [\App\Rpc\UserHandler::class, 'get'],
        'user.update' => [\App\Rpc\UserHandler::class, 'update'],
    ],
];

Resolve handlers through the Laravel container. The bundle's Symfony-side container wiring is inert in Laravel. Instead, resolve handlers per call so constructor injection works:

$entry = config("rpc.methods.{$method}");
[$class, $action] = $entry;
$handler = app($class);          // constructor DI works
$result  = $handler->{$action}($params);

Keep handlers transport-agnostic. Handlers should take a plain array and return arrays/scalars — no Request, no Response. This makes them unit-testable and lets you mount the same handlers behind HTTP, a queue consumer, or a CLI command.

Validate inside the handler and map errors deliberately. JSON-RPC 2.0 reserves codes:

Code Meaning When
-32700 Parse error malformed JSON body
-32600 Invalid Request not an object / bad jsonrpc field
-32601 Method not found unknown method name
-32602 Invalid params validation failure
-32603 Internal error uncaught exception

Wrap validation in a RpcException carrying the code; catch it at the controller boundary.

Support batch and notification in one code path. If the payload is a list, iterate and collect responses; if a single request has no id, it's a notification — process it and return HTTP 204 with an empty body. Skipping this is the most common way JSON-RPC servers break JavaScript clients.

Auth and tenancy belong in middleware, not handlers. Put the resolved user/tenant on the container or pass it into the handler constructor:

$this->app->bind(RpcContext::class, fn () => new RpcContext(auth()->user()));

Test with feature tests, not mocks. Post raw JSON and assert the envelope:

$this->postJson('/api/rpc', [
    'jsonrpc' => '2.0', 'method' => 'user.get',
    'params' => ['id' => 1], 'id' => 1,
])->assertOk()->assertJsonPath('result.id', 1);

Use Http::fake() only when you're on the client side calling a third-party JSON-RPC endpoint.

Gotchas and Tips

2.0.0 is a major bump with no release notes. The only artifact is a 1.0.0...2.0.0 compare link. Do not upgrade blind. Before touching composer.json:

  • Read the compare diff end-to-end: https://github.com/tourze/json-rpc-container-bundle/compare/1.0.0...2.0.0
  • Check for a PHP minimum-version bump in composer.json — a major on a low-traffic package usually coincides with dropping old PHP support.
  • Check for renames of the dispatcher/server classes and changes to the method-map shape. Those are the two things 2.0-style bumps in DI-bundle packages typically break.
  • If the diff is large and undocumented, budget for reading the class signatures rather than trusting the changelog.

The bundle will not auto-register in Laravel. Laravel's package discovery looks for Illuminate\Support\ServiceProvider in extra.laravel.providers. A Symfony bundle has none. If you composer require it and nothing happens, that's expected — you're using it as a library, not an installed package.

HTTP status vs JSON-RPC error. A JSON-RPC error (bad params, unknown method) must be returned with HTTP 200 and an error member in the body. Laravel's exception handler will happily convert your ValidationException into a 422, which strict clients treat as a transport failure. Catch at the controller boundary and render the envelope yourself.

$request->json() returns null on a bad Content-Type. Clients that omit Content-Type: application/json will produce a null payload that looks like a parse error. Read $request->getContent() and json_decode it yourself if you want to be forgiving; otherwise return -32700 explicitly rather than letting a null propagate.

Positional vs named params. JSON-RPC 2.0 allows params to be an array (positional) or an object (named). Most implementations only handle one. Decide which you support, document it, and reject the other with -32602 — silent misbehavior here is painful to debug from the client side.

Don't resolve the container at boot. Building the method map in a service provider's register() and eagerly instantiating handlers will pull every handler's dependencies on every request, including for routes that never touch RPC. Store the map as config and resolve lazily.

Extension points to look for in the source: a method resolver interface (swap in an attribute/annotation-based scanner), an error formatter (normalize envelopes), and a middleware or event hook (attach request IDs / tracing). If 2.0 introduced an interface for any of these, wire to the interface, not the concrete class — that's what will keep you stable through the next major.

Log the raw request body on -32603 only. Logging every payload is noisy and risks leaking parameters; logging nothing makes production incidents unreadable.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource