Overview
When loading a detail form with multiple comboboxes (e.g., 7 dropdowns + business data), each typically needs a separate RPC call. The Batch RPC Service solves this by:
- Collecting calls - Queue multiple RPC calls before sending
- Single request - Send all calls in one message over WebSocket/HTTP
- Backend parallelization - Backend can process all calls in parallel
- Independent resolution - Each call's promise resolves/rejects independently
Basic Usage
Live Demo: Batch vs Sequential
Compare the performance difference between batched and sequential requests. The mock transport simulates 300ms network delay per request.
Error Handling: Partial Failures
Each call in a batch resolves or rejects independently. If one call fails, others still succeed.
Error Handling Pattern
Transforming Results
Use standard Promise .then() chaining to transform results before awaiting.
This is useful when endpoints return arrays for single items or nested response structures.
batch.add() returns a Promise, you can use any Promise method: .then(), .catch(), .finally(), or combine with Promise.all().Transport Adapters
The batch RPC is transport-agnostic. Use the factory functions to create adapters for your backend.
SignalR (ASP.NET)
Phoenix Channels (Elixir)
HTTP/REST
Transport Authentication
Each transport type handles authentication differently. HTTP transport supports dynamic headers and interceptors, while SignalR and Phoenix configure auth on the connection itself.
HTTP: Dynamic Headers & Cookies
Use getHeaders callback for tokens that may change, and fetchOptions.credentials for cookies.
HTTP: Request/Response Interceptors
Use interceptors for logging, metrics, or handling specific status codes like 401.
SignalR: Access Token Factory
Auth is configured on the connection builder, not the transport.
Phoenix: Socket Params
Auth is configured on the socket connection, not the transport.
Request Metadata (Session ID, Tenant ID, etc.)
Use getRequestMetadata to add contextual data to every batch request body.
This is useful for session tracking, multi-tenancy, or debugging.
Adding Metadata to Requests
Server-Generated Session ID Example
Session ID generated on server, passed to client, displayed in footer, and sent with every request.
getRequestMetadata is available on createHttpTransport, createSignalRTransport, and createPhoenixTransport.Configuration Presets
Two presets are provided for common response patterns:
commonResponseConfig
For ASP.NET with CommonResponse wrapper pattern.
pureDataConfig
For Phoenix or clean REST APIs returning data directly.
Real-World Example: Order Form
Here's how you'd use batch RPC to load all data for an order detail form:
Wire Protocol
The batch RPC uses a simple JSON protocol for requests and responses.
Request Format
Response Format
API Reference
createBatch(transport)
Creates a new batch for collecting and executing RPC calls.
| Method | Description |
|---|---|
add<T>(method, params?) | Add a call to the batch. Returns Promise<T> that resolves after execute(). |
execute() | Send all queued calls as a single request. Returns Promise<void>. |
size | Number of calls currently in the batch (readonly). |
executed | Whether the batch has been executed (readonly). |
Transport Factories
| Function | Description |
|---|---|
createSignalRTransport(connection, config) | Create transport for ASP.NET SignalR hubs. |
createPhoenixTransport(channel, config) | Create transport for Elixir Phoenix channels. |
createHttpTransport(url, config, options?) | Create transport for REST API endpoints. |
RpcTransportConfig
| Property | Type | Description |
|---|---|---|
parseResponse | (raw) => BatchResponse | Parse raw transport response into BatchResponse format. |
parseCallResult? | (raw) => unknown | Unwrap individual call results (e.g., CommonResponse.data). |
isError? | (result) => boolean | Determine if a result is an error. Default: checks for error field. |
parseError? | (result) => RpcError | Extract error details from a failed result. |
HttpTransportOptions
| Property | Type | Description |
|---|---|---|
headers? | Record<string, string> | Static headers to include with each request. |
getHeaders? | () => Record | Promise | Dynamic headers callback - called before each request. Use for auth tokens. |
getRequestMetadata? | () => Record | Promise | Request metadata callback - adds meta field to request body (sessionId, tenantId, etc.). |
onBeforeRequest? | (request, init) => void | Request interceptor - called before each request is sent. |
onAfterResponse? | (response) => void | Response interceptor - handle status codes, logging, etc. |
fetchOptions? | RequestInit | Additional fetch options (e.g., credentials: 'include'). |
fetchFn? | typeof fetch | Custom fetch function. Default: globalThis.fetch. |
timeout? | number | Request timeout in milliseconds. Default: 30000. |
SignalRTransportOptions / PhoenixTransportOptions
| Property | Type | Description |
|---|---|---|
methodName? / eventName? | string | Hub method or channel event name. Default: "BatchInvoke" / "batch_invoke". |
getRequestMetadata? | () => Record | Promise | Request metadata callback - adds meta field to request body. |
timeout? | number | Request timeout in milliseconds (Phoenix only). Default: 30000. |