@insler/rpc/client
The caller side. Client.create(contract, transport, options?) builds a
fully-typed client whose methods are derived from the contract; the wire it
calls through is any ClientTransport — the in-memory transport or an
adapter like NATS.
import { Client } from '@insler/rpc/client';
const accounts = Client.create(Accounts, clientTransport);await accounts.getBalance({ accountId: 'a_1' }); // { balance: number }Error modes
Section titled “Error modes”- Throw mode (default): a failed call throws
ContractError— best ergonomics. - Result mode (
errors: 'result'): calls return{ ok, value } | { ok, error }with the error union typed per method — handle typed errors without try/catch.
Scoped clients
Section titled “Scoped clients”Client.withContext(client, ctx) returns a scoped client with context
pre-applied (works in both error modes):
const asUser = Client.withContext(accounts, { identity: { userId: 'u_1' } });await asUser.getBalance({ accountId: 'a_1' });Middleware
Section titled “Middleware”options.middleware composes cross-cutting per-call logic (auth, logging,
timing) via composeMiddleware; middleware executes in array order,
outermost first. Coverage today is partial: unary and serverStream calls
route through the chain, while clientStream and duplex still call the
transport directly — do not assume uniform coverage on streaming interceptors
yet.
Secondary entrypoints
Section titled “Secondary entrypoints”@insler/rpc/client/test—TestTransportfor unit-testing client logic without a host.@insler/rpc/client/dev— logging and timing middleware for development.
Use it well
Section titled “Use it well”Let the contract infer method signatures — never hand-type them. Keep business validation on the host, not in client middleware. Streaming methods require a transport that implements the matching stream invocation; absence throws a clear error.