datto/json-rpc
Lightweight PHP library for building and parsing JSON-RPC 2.0 messages. Fully spec compliant, 100% unit-tested, and transport-agnostic so you can use HTTP, SSH, or any channel. Includes simple Client/Server APIs and working examples.
Install the package:
composer require datto/json-rpc
Basic Client Usage:
use Datto\JsonRpc\Client;
$client = new Client();
$response = $client->query(1, 'add', [1, 2])->encode();
// Returns: {"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}
Basic Server Usage:
use Datto\JsonRpc\Api;
use Datto\JsonRpc\Server;
$api = new Api();
$api->addMethod('add', function ($params) {
return array_sum($params);
});
$server = new Server($api);
$reply = $server->reply('{"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}');
// Returns: {"jsonrpc":"2.0","result":3,"id":1}
Use this package to wrap legacy REST APIs or microservices into a JSON-RPC interface:
$client = new Client();
$response = $client->query(1, 'fetchUser', ['userId' => 123])->encode();
Chaining Methods:
$client->query(1, 'add', [1, 2])->encode()->sendOverHttp();
Batch Requests:
$client->query(1, 'method1', [])->query(2, 'method2', []);
$batch = $client->encode();
Custom Encoding/Decoding:
$client->preEncode(function ($message) {
// Modify message before encoding
return $message;
});
$client->postDecode(function ($response) {
// Process response after decoding
return $response;
});
Dynamic Method Registration:
$api = new Api();
$api->addMethod('dynamicMethod', function ($params) {
return "Processed: " . json_encode($params);
});
Error Handling:
$api->addMethod('divide', function ($params) {
if ($params[1] === 0) {
throw new \Exception("Division by zero");
}
return $params[0] / $params[1];
});
Middleware for Requests:
$server = new Server($api);
$server->setMiddleware(function ($request) {
// Log or validate request
return $request;
});
Route Handling:
Route::post('/jsonrpc', function (Request $request) {
$api = new Api();
$api->addMethod('laravelMethod', function () {
return ['data' => 'from Laravel'];
});
$server = new Server($api);
return response()->json($server->reply($request->getContent()));
});
Service Container Binding:
$app->bind('jsonrpc.api', function () {
$api = new Api();
$api->addMethod('appMethod', function () {
return app('someService')->doSomething();
});
return $api;
});
Transport Layer Missing:
The package only handles serialization/deserialization. You must implement your own transport (HTTP, SSH, etc.) or use a companion package like datto/json-rpc-http.
ID Handling:
Notify vs Query:
notify() methods do not expect a response (no ID required).query() methods require an ID and expect a response.Error Responses:
decode() method returns ErrorResponse or ResultResponse objects.if ($response instanceof ErrorResponse) {
throw new \Exception($response->getMessage());
}
Validate JSON-RPC Input:
Use Client::decode() to validate raw JSON-RPC strings before processing:
try {
$responses = $client->decode($rawJsonRpc);
} catch (ErrorException $e) {
// Invalid JSON-RPC input
}
Inspect Raw Messages:
Use preEncode/postDecode to log or inspect messages:
$client->preEncode(function ($message) {
\Log::debug('Outgoing RPC:', ['message' => $message]);
return $message;
});
Batch Request Quirks:
Custom Response Classes:
Extend Response, ResultResponse, or ErrorResponse to add metadata:
class CustomResponse extends ResultResponse {
public function getMetadata() {
return $this->metadata ?? [];
}
}
Transport Abstraction:
Use Client::rawReply() and Server::rawReply() to integrate with custom transports:
$client->rawReply($rawResponse); // Bypass default decoding
Middleware for Servers:
Override Server::reply() to add cross-cutting concerns (auth, logging):
$server->setMiddleware(function ($request) {
if (!auth()->check()) {
throw new \Exception("Unauthorized");
}
return $request;
});
CSRF Protection: JSON-RPC endpoints may conflict with Laravel’s CSRF middleware. Exclude them:
Route::post('/jsonrpc', function () { ... })->middleware('jsonrpc');
(Create a custom middleware to bypass CSRF.)
Request Parsing:
Laravel’s Request object may not parse raw JSON-RPC payloads correctly. Use:
$rawPayload = $request->getContent();
$responses = $client->decode($rawPayload);
Service Container Conflicts:
Avoid naming collisions with Laravel’s container bindings (e.g., api is a reserved key). Use:
$app->bind('jsonrpc.api', function () { ... });
How can I help you explore Laravel packages today?