Clients
Creating a Client
Section titled “Creating a Client”JSON-RPC Client
Section titled “JSON-RPC Client”Create a client using the static factory method:
use Cline\RPC\Clients\Client;
$client = Client::json('https://api.example.com/rpc');XML-RPC Client
Section titled “XML-RPC Client”For legacy XML-RPC endpoints:
$client = Client::xml('https://api.example.com/xmlrpc');Generic Client
Section titled “Generic Client”Create a client with custom protocol:
use Cline\RPC\Protocols\JsonRpcProtocol;
$client = Client::create( 'https://api.example.com/rpc', new JsonRpcProtocol());Single Requests
Section titled “Single Requests”Basic Request
Section titled “Basic Request”Make a single RPC call:
use Cline\RPC\Data\RequestObjectData;
$client = Client::json('https://api.example.com/rpc');
$request = RequestObjectData::asRequest( 'app.user_get', ['user_id' => 42], 1);
$response = $client->add($request)->request();
if ($response->hasResult()) { $user = $response->result; echo $user['name'];}
if ($response->hasError()) { $error = $response->error; echo "Error {$error->code}: {$error->message}";}Without ID (Notification)
Section titled “Without ID (Notification)”Send a notification (no response expected):
$request = RequestObjectData::asNotification( 'app.log_event', ['event' => 'user_login', 'user_id' => 42]);
$client->add($request)->request();Inline Request Creation
Section titled “Inline Request Creation”$response = $client ->add( RequestObjectData::asRequest( 'app.user_get', ['user_id' => 42], 1 ) ) ->request();Batch Requests
Section titled “Batch Requests”Multiple Requests
Section titled “Multiple Requests”Execute multiple requests in a single HTTP call:
$client = Client::json('https://api.example.com/rpc');
$responses = $client ->add( RequestObjectData::asRequest( 'app.user_get', ['user_id' => 1], 1 ) ) ->add( RequestObjectData::asRequest( 'app.user_get', ['user_id' => 2], 2 ) ) ->add( RequestObjectData::asRequest( 'app.user_list', ['cursor' => ['limit' => 10]], 3 ) ) ->request();
// Responses is a DataCollectionforeach ($responses as $response) { if ($response->hasResult()) { var_dump($response->result); }}Using addMany()
Section titled “Using addMany()”Add multiple requests at once:
$requests = [ RequestObjectData::asRequest( 'app.user_get', ['user_id' => 1], 1 ), RequestObjectData::asRequest( 'app.user_get', ['user_id' => 2], 2 ),];
$responses = $client->addMany($requests)->request();Matching Responses to Requests
Section titled “Matching Responses to Requests”Match responses by ID:
$responses = $client ->add( RequestObjectData::asRequest( 'app.user_get', ['user_id' => 1], 'user-1' ) ) ->add( RequestObjectData::asRequest( 'app.post_get', ['post_id' => 100], 'post-100' ) ) ->request();
foreach ($responses as $response) { match ($response->id) { 'user-1' => $this->handleUser($response->result), 'post-100' => $this->handlePost($response->result), };}Response Handling
Section titled “Response Handling”Checking for Success
Section titled “Checking for Success”$response = $client->add($request)->request();
if ($response->hasResult()) { // Success - process result $data = $response->result;}
if ($response->hasError()) { // Error occurred $error = $response->error; echo "Code: {$error->code}"; echo "Message: {$error->message}";
if ($error->data) { var_dump($error->data); }}Standard Error Codes
Section titled “Standard Error Codes”JSON-RPC 2.0 defines standard error codes:
if ($response->hasError()) { $error = $response->error;
match ($error->code) { -32700 => 'Parse error', -32600 => 'Invalid request', -32601 => 'Method not found', -32602 => 'Invalid params', -32603 => 'Internal error', default => "Application error: {$error->message}", };}Working with Pagination
Section titled “Working with Pagination”Fetching Paginated Results
Section titled “Fetching Paginated Results”$client = Client::json('https://api.example.com/rpc');
$cursor = null;$allUsers = [];
do { $response = $client ->add( RequestObjectData::asRequest( 'app.user_list', [ 'cursor' => [ 'limit' => 50, 'cursor' => $cursor, ], ], 1 ) ) ->request();
$result = $response->result; $allUsers = array_merge($allUsers, $result['data']);
$cursor = $result['meta']['next_cursor'] ?? null;} while ($cursor !== null);
echo "Fetched " . count($allUsers) . " users";Parallel Pagination
Section titled “Parallel Pagination”Fetch multiple pages simultaneously:
$client = Client::json('https://api.example.com/rpc');
// Fetch first 3 pages in parallel$responses = $client ->add( RequestObjectData::asRequest( 'app.user_list', ['cursor' => ['limit' => 50, 'cursor' => null]], 1 ) ) ->add( RequestObjectData::asRequest( 'app.user_list', ['cursor' => ['limit' => 50, 'cursor' => 'page2']], 2 ) ) ->add( RequestObjectData::asRequest( 'app.user_list', ['cursor' => ['limit' => 50, 'cursor' => 'page3']], 3 ) ) ->request();
$allUsers = $responses->flatMap(fn($r) => $r->result['data']);Advanced Usage
Section titled “Advanced Usage”Custom HTTP Configuration
Section titled “Custom HTTP Configuration”Customize the underlying HTTP client:
use Illuminate\Support\Facades\Http;
$client = new Client( host: 'https://api.example.com/rpc', protocol: new JsonRpcProtocol());
// Access the underlying HTTP client$httpClient = Http::baseUrl('https://api.example.com/rpc') ->timeout(30) ->withHeaders([ 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json', ]) ->retry(3, 100);Error Recovery
Section titled “Error Recovery”Handle errors gracefully with retries:
function callWithRetry(Client $client, RequestObjectData $request, int $maxRetries = 3): mixed{ $attempt = 0;
while ($attempt < $maxRetries) { $response = $client->add($request)->request();
if ($response->hasResult()) { return $response->result; }
if ($response->hasError()) { $error = $response->error;
// Don't retry client errors if ($error->code >= -32602 && $error->code <= -32600) { throw new Exception("Client error: {$error->message}"); }
// Retry server errors $attempt++; sleep(pow(2, $attempt)); // Exponential backoff } }
throw new Exception('Max retries exceeded');}Practical Examples
Section titled “Practical Examples”Type-Safe Client Wrapper
Section titled “Type-Safe Client Wrapper”Create a typed client for your API:
<?php
namespace App\Services;
use Cline\RPC\Clients\Client;use Cline\RPC\Data\RequestObjectData;
class UserApiClient{ private Client $client; private int $requestId = 1;
public function __construct(string $baseUrl, string $apiToken) { $this->client = Client::json($baseUrl); // Configure authentication here }
public function getUser(int $userId): array { $response = $this->client ->add( RequestObjectData::asRequest( 'app.user_get', ['user_id' => $userId], $this->requestId++ ) ) ->request();
if ($response->hasError()) { throw new Exception($response->error->message); }
return $response->result; }
public function listUsers(int $limit = 20, ?string $cursor = null): array { $response = $this->client ->add( RequestObjectData::asRequest( 'app.user_list', [ 'cursor' => [ 'limit' => $limit, 'cursor' => $cursor, ], ], $this->requestId++ ) ) ->request();
if ($response->hasError()) { throw new Exception($response->error->message); }
return $response->result; }
public function createUser(array $userData): array { $response = $this->client ->add( RequestObjectData::asRequest( 'app.user_create', $userData, $this->requestId++ ) ) ->request();
if ($response->hasError()) { throw new Exception($response->error->message); }
return $response->result; }
public function batchGetUsers(array $userIds): array { $client = $this->client;
foreach ($userIds as $userId) { $client->add( RequestObjectData::asRequest( 'app.user_get', ['user_id' => $userId], $this->requestId++ ) ); }
$responses = $client->request();
return $responses ->filter(fn($r) => $r->hasResult()) ->map(fn($r) => $r->result) ->all(); }}Usage:
$api = new UserApiClient('https://api.example.com/rpc', 'token');
// Get single user$user = $api->getUser(42);
// List users$users = $api->listUsers(limit: 50);
// Create user$newUser = $api->createUser([ 'name' => 'Alice', 'email' => 'alice@example.com',]);
// Batch fetch$users = $api->batchGetUsers([1, 2, 3, 4, 5]);Caching Client
Section titled “Caching Client”Add caching to reduce API calls:
use Illuminate\Support\Facades\Cache;
class CachedUserApiClient extends UserApiClient{ public function getUser(int $userId): array { return Cache::remember( "user.{$userId}", now()->addMinutes(5), fn() => parent::getUser($userId) ); }}Async Client
Section titled “Async Client”Process batch requests asynchronously:
use Illuminate\Support\Facades\Http;
$promises = collect([1, 2, 3, 4, 5])->mapWithKeys(function ($userId) { return [ "user-{$userId}" => Http::async() ->post('https://api.example.com/rpc', [ 'jsonrpc' => '2.0', 'method' => 'app.user_get', 'params' => ['user_id' => $userId], 'id' => $userId, ]), ];});
$responses = Http::pool(fn($pool) => $promises->all());
foreach ($responses as $key => $response) { $data = $response->json(); if (isset($data['result'])) { echo "Got user data for {$key}\n"; }}