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

Client Laravel Package

sajya/client

A lightweight JSON-RPC client for Laravel and PHP that provides an expressive API for calling remote RPC methods and handling structured responses, making it easy to integrate external JSON-RPC services into your application.

View on GitHub
Deep Wiki
Context7

Getting Started

sajya/client is the client-side counterpart to sajya/server: a small JSON-RPC 2.0 client built on top of Laravel's HTTP client. Reach for it when you are calling a JSON-RPC endpoint (usually another Laravel service using sajya/server) instead of hand-rolling Http::post() payloads.

Minimal setup:

composer require sajya/client

Publish the config if you need to change defaults or register named endpoints:

php artisan vendor:publish --tag=client-config

The first use case to get working is a single procedure call against an endpoint you control:

use Sajya\Client\Client;

$client = new Client('https://billing.internal/rpc');

$response = $client->call('math@sum', [1, 2]);

Where to look first:

  1. config/client.php (after publishing) — endpoint URLs, default headers, timeouts.
  2. The Sajya\Client\Client class — this is the whole API surface; it is small, read it end to end before building abstractions on top of it.
  3. The sajya/server docs for the procedure naming convention (namespace@method) and parameter binding, since the client is only half the contract.

Release note — 1.5.0: this release only widens the illuminate/http and illuminate/support version constraints (plus CI housekeeping). There are no API changes, so it is a safe drop-in upgrade and mainly matters because it restores compatibility with newer Laravel releases. Check your composer.json constraints if you were previously pinned to an older Laravel line.

Implementation Patterns

Bind a configured client, don't new it everywhere. Constructing new Client($url) in every controller makes tests and multi-endpoint setups painful. Register a named instance in a service provider:

$this->app->singleton(BillingClient::class, fn () => new Client(
    config('services.billing.rpc_url'),
));

Then inject BillingClient (or a thin wrapper around Client) wherever needed.

Wrap procedures in typed methods. JSON-RPC calls are stringly-typed by nature; a small wrapper class per remote service keeps procedure names and payload shapes in one place:

class BillingClient
{
    public function __construct(private Client $client) {}

    public function charge(int $invoiceId, int $amount): array
    {
        return $this->client->call('invoices@charge', [
            'invoice_id' => $invoiceId,
            'amount' => $amount,
        ]);
    }
}

This gives you IDE completion, a single place to update when the remote procedure signature changes, and an obvious seam for Http::fake() in tests.

Positional vs named params matters. JSON-RPC 2.0 accepts both a list (positional) and an object (named). [1, 2] and ['a' => 1, 'b' => 2] are not interchangeable on the server side — match whatever the server's procedure signature expects, and be consistent within a service wrapper.

Batch related calls. If your version supports it, batching amortises the round trip when a page needs several independent lookups. Keep batch members independent: a single failing member should not invalidate the whole batch's usefulness.

Fire-and-forget notifications. For audit/log pings where you do not need a result, send without an id (JSON-RPC notification semantics) so the server does not build a response.

Auth and transport config. Most internal RPC endpoints are behind a token or mTLS. Prefer setting default headers/timeouts once (config or a constructor argument) over per-call plumbing. If the endpoint is flaky, wrap calls with retry logic — but only for idempotent procedures; retrying invoices@charge blindly is how you double-bill customers.

Testing with Http::fake(). Because the client rides on Laravel's HTTP client, you can assert the outgoing JSON-RPC envelope without a live server:

Http::fake(['*/rpc' => Http::response([
    'jsonrpc' => '2.0', 'id' => 1, 'result' => 3,
])]);

$this->post('/checkout')->assertOk();

Http::assertSent(fn ($request) => $request['method'] === 'math@sum');

Gotchas and Tips

HTTP 200 does not mean success. JSON-RPC errors are returned in the response body (error.code, error.message, error.data) with a 200 status. Never branch on $response->successful() alone — inspect the envelope. Wrap the client so callers get a Result/exception, not a raw array they might misread.

Transport errors are a different beast. A 500, timeout, or connection refusal is not a JSON-RPC error; it means you never reached the RPC layer. Handle both classes separately, otherwise a downed service looks like a validation failure.

ID correlation in batches. Batch responses can come back in any order. If you correlate by array index you will eventually swap results; correlate by id.

Don't rely on status codes for validation. Server-side sajya/server validation failures surface as JSON-RPC error objects with structured data, not 422s. Parse error.data if you want to map them to form errors.

Keep secrets out of published config. Put endpoint URLs in config/services.php/config/client.php but read tokens from .env. Published config files are committed.

Debug by logging the raw exchange. When a procedure "returns nothing", log the raw request and response bodies (Laravel's Http::withOptions(['debug' => ...]) or a temporary Http::fake() dump). Nine times out of ten the issue is a mismatched procedure name (math@sum vs math.sum) or a params shape mismatch, both visible only in the raw envelope.

Extension points. The Client class is intentionally thin — subclass or decorate it rather than patching vendor code. A decorator that adds logging, metrics, retries, and error normalisation is the natural place for cross-cutting concerns, keeping the underlying client untouched across upgrades (relevant here, since 1.5.0 was a constraint-only release and future patches are likely to stay small).

Upgrade note. 1.5.0 is a dependency-range bump; run composer update sajya/client and your existing test suite is the right safety net. There is nothing to migrate, but verify your lockfile actually moved the illuminate/* constraints if you were blocked on an older Laravel version.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin