Choosing a Transport¶
Every transport implements IRpcTransport. Swapping transports requires changing only the transport construction — service and contract code stays identical.
Comparison¶
| Transport | Package | Platforms | Streaming | Zero-copy | Use case |
|---|---|---|---|---|---|
| InMemory | rpc_dart |
all | all patterns | yes | Testing, same-process services |
| HTTP/2 | rpc_dart_http2 |
native | all patterns | no | gRPC-compatible, production server-to-server |
| HTTP/1.1 | rpc_dart_http |
all (web/wasm) | unary only | no | Web clients, simple REST-like APIs |
| WebSocket | rpc_dart_websocket |
all (web/wasm) | all patterns | no | Real-time, browser clients |
| Isolate | rpc_dart_isolate |
IO + web workers | all patterns | yes | CPU-bound work in parallel isolates |
Decision Guide¶
Testing or co-located services in the same process? → Use InMemory. Zero overhead, zero-copy, no ports.
Native server — need all RPC patterns, TLS, gRPC interop? → Use HTTP/2.
Browser or web/wasm client? → Use WebSocket for full streaming, or HTTP/1.1 for unary-only with maximum compatibility.
CPU-intensive work that would block the main isolate? → Use Isolate to offload to a background worker.
Changing Transports¶
The only change required is the transport construction. Everything else stays the same:
// Testing
final (callerTransport, responderTransport) = RpcInMemoryTransport.pair();
// Production — HTTP/2
final callerTransport = await RpcHttp2CallerTransport.secureConnect(
host: 'api.example.com',
port: 443,
);
// Production — WebSocket
final callerTransport = RpcWebSocketCallerTransport.connect(
Uri.parse('wss://api.example.com/rpc'),
);
// In all cases, the caller and endpoint construction is identical:
final callerEndpoint = RpcCallerEndpoint(transport: callerTransport);
final caller = MyServiceCaller(callerEndpoint);