Skip to content

Message Protocol

This page documents the low-level message protocol used over the MessagePort. You do not need to know this to use the library, but it is useful for debugging or building custom integrations.

Sent when the client initiates a query, mutation, or subscription.

interface TRPCPortRequest {
kind: 'request';
id: number;
method: 'query' | 'mutation' | 'subscription';
path: string;
input: unknown;
lastEventId?: string;
}
FieldTypeDescription
kind'request'Message discriminator
idnumberUnique request identifier (monotonically increasing)
method'query' | 'mutation' | 'subscription'The tRPC procedure type
pathstringDot-separated procedure path (e.g., 'user.getById')
inputunknownThe procedure input, transferred via Structured Clone
lastEventIdstring | undefinedFor subscription resumption (optional)

Sent when the client unsubscribes from an active subscription.

interface TRPCPortSubscriptionStop {
kind: 'subscription.stop';
id: number;
}
FieldTypeDescription
kind'subscription.stop'Message discriminator
idnumberThe ID of the subscription to stop

Sent when a procedure returns data or a subscription yields a value.

interface TRPCPortResultData {
kind: 'result';
id: number;
type: 'data';
data: unknown;
eventId?: string;
}
FieldTypeDescription
kind'result'Message discriminator
idnumberThe request ID this result belongs to
type'data'Result type discriminator
dataunknownThe procedure result, transferred via Structured Clone
eventIdstring | undefinedThe tracked event ID for resumable subscription data

For queries and mutations, a single data result is sent, and the request is complete.

For subscriptions, multiple data results may be sent over time.

When a subscription yields tracked(id, data), the protocol sends eventId: id and data: { id, data }, matching tRPC v11’s tracked subscription output shape.

Sent after a subscription procedure has started successfully.

interface TRPCPortResultStarted {
kind: 'result';
id: number;
type: 'started';
}
FieldTypeDescription
kind'result'Message discriminator
idnumberThe subscription ID
type'started'Indicates the subscription has started

Sent when a subscription ends naturally (the async iterable completes).

interface TRPCPortResultStopped {
kind: 'result';
id: number;
type: 'stopped';
}
FieldTypeDescription
kind'result'Message discriminator
idnumberThe subscription ID
type'stopped'Indicates the subscription has ended

Sent when a procedure throws an error.

interface TRPCPortError {
kind: 'error';
id: number;
error: {
code: number;
message: string;
data: unknown;
};
}
FieldTypeDescription
kind'error'Message discriminator
idnumberThe request ID that caused the error
error.codenumbertRPC error code (matches HTTP status codes)
error.messagestringHuman-readable error message
error.dataunknownAdditional error metadata (tRPC error shape data)
Client Server
| |
| { kind: 'request', |
| method: 'query', |
| id: 1, path, input } |
|------------------------------>|
| |
| { kind: 'result', |
| id: 1, type: 'data', |
| data } |
|<------------------------------|
Client Server
| |
| { kind: 'request', |
| method: 'subscription', |
| id: 1, path, input } |
|------------------------------>|
| |
| { kind: 'result', |
| id: 1, type: 'started' } |
|<------------------------------|
| { kind: 'result', |
| id: 1, type: 'data', ..} |
|<------------------------------|
| { kind: 'result', |
| id: 1, type: 'data', ..} |
|<------------------------------|
| |
| { kind: 'subscription.stop',|
| id: 1 } |
|------------------------------>|
| |
| // Or, if the async iterable |
| // completes naturally: |
| { kind: 'result', |
| id: 1, type: 'stopped' } |
|<------------------------------|