Go Guide
The Go runtime lives in trevrpc-go/ with module path trev.zip/llc/trevrpc/trevrpc-go. It provides client helpers, server routing, quic-go and WebTransport integration, shared wire types, status handling, metadata validation, and stream helpers.
Generated Service Shape
For the greeter example, generated Go code has the same shape as this hand-written example:
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
LotsOfReplies(context.Context, *HelloRequest) (trevrpc.MessageStream[*HelloReply], error)
LotsOfGreetings(context.Context, trevrpc.MessageStream[*HelloRequest]) (*HelloReply, error)
BidiHello(context.Context, trevrpc.MessageStream[*HelloRequest]) (trevrpc.MessageStream[*HelloReply], error)
}
Generated clients accept the routine *trevrpc.Channel through the trevrpc.Transport interface and optional default call options:
client := greeter.NewGreeterClient(
channel,
trevrpc.WithTimeout(5*time.Second),
)
Implement a Service
type greeterService struct{}
func (greeterService) SayHello(_ context.Context, request *greeter.HelloRequest) (*greeter.HelloReply, error) {
return &greeter.HelloReply{Message: "hello " + request.Name}, nil
}
func (greeterService) LotsOfReplies(_ context.Context, request *greeter.HelloRequest) (trevrpc.MessageStream[*greeter.HelloReply], error) {
return trevrpc.FromSlice(
&greeter.HelloReply{Message: "hello " + request.Name},
&greeter.HelloReply{Message: "welcome to TrevRPC over QUIC"},
), nil
}
Client-streaming handlers can range over all request messages:
func (greeterService) LotsOfGreetings(_ context.Context, requests trevrpc.MessageStream[*greeter.HelloRequest]) (*greeter.HelloReply, error) {
var names []string
for request, err := range trevrpc.Messages(requests) {
if err != nil {
return nil, err
}
names = append(names, request.Name)
}
return &greeter.HelloReply{Message: "hello, " + strings.Join(names, ", ")}, nil
}
Register a Server
server := trevrpc.NewServer()
greeter.RegisterGreeterServer(server, greeterService{})
Attach authorization or metrics before serving:
server.SetAuthorizer(trevrpc.BearerAuthorizer("trevrpc-example-token"))
server.SetMetrics(myMetrics{})
Serve a quic-go listener:
listener, err := quic.ListenAddr("127.0.0.1:50051", tlsConfig, &quic.Config{})
if err != nil {
log.Fatal(err)
}
defer listener.Close()
if err := trevrpc.ServeQUIC(context.Background(), listener, server); err != nil {
log.Fatal(err)
}
TLS, certificates, client identity, and QUIC transport configuration remain the application's responsibility. Set NextProtos: []string{trevrpc.ALPN} in the TLS config.
To serve ordinary HTTP/3, set ServerOptions.EnableHTTP3 = true, advertise HTTP/3 ALPN, and pass server.Options() through QUICServerConfig. To also serve WebTransport, set EnableWebTransport, install a non-nil WebTransportAdmission, and use the same listener. See HTTP/3 and WebTransport.
Build a Client
channel, err := trevrpc.Dial(
ctx,
"127.0.0.1:50051",
trevrpc.DialOptions{TLSConfig: tlsConfig},
)
if err != nil {
log.Fatal(err)
}
defer channel.Close()
client := greeter.NewGreeterClient(
channel,
trevrpc.WithTimeout(5*time.Second),
trevrpc.WithMetadata("authorization", []byte("Bearer trevrpc-example-token")),
)
response, err := client.SayHello(ctx, &greeter.HelloRequest{Name: "TrevRPC"})
trevrpc.Dial returns a long-lived *trevrpc.Channel. An address target uses native QUIC; an https:// target uses WebTransport. Every RPC snapshots one ready generation. If it dies, in-flight calls fail without retry, replay, resumption, or movement to another connection. New calls fail immediately with Unavailable while reconnecting; call channel.WaitUntilReady(ctx) before issuing work when waiting is intentional.
Routine channels use fixed reconnect defaults: 100 ms initial delay, multiplier 2, 20 percent jitter, and a 30 second cap. DialOptions does not expose reconnect-policy tuning.
For advanced integrations, benchmarks, diagnostics, or deterministic tests that explicitly own one established connection, use the raw transport:
conn, err := quic.DialAddr(ctx, "127.0.0.1:50051", tlsConfig, &quic.Config{})
if err != nil {
log.Fatal(err)
}
defer conn.CloseWithError(0, "client done")
client := greeter.NewGreeterClient(trevrpc.Advanced.NewRawQUICClient(conn))
Client-streaming and bidirectional-streaming client methods return call objects:
greetings, err := client.LotsOfGreetings(ctx)
if err != nil {
log.Fatal(err)
}
greetings.Send(&greeter.HelloRequest{Name: "first"})
greetings.Send(&greeter.HelloRequest{Name: "second"})
summary, err := greetings.CloseAndRecv()
bidi, err := client.BidiHello(ctx)
if err != nil {
log.Fatal(err)
}
bidi.Send(&greeter.HelloRequest{Name: "first"})
reply, err := bidi.Recv()
bidi.CloseSend()
Generated clients also expose FromStream helpers for fixed or prebuilt request streams:
summary, err := client.LotsOfGreetingsFromStream(
ctx,
trevrpc.FromSlice(
&greeter.HelloRequest{Name: "first"},
&greeter.HelloRequest{Name: "second"},
),
)
For routine WebTransport, pass the HTTPS URL directly to Dial:
channel, err := trevrpc.Dial(
ctx,
"https://127.0.0.1:50051/trevrpc",
trevrpc.DialOptions{TLSConfig: tlsConfig},
)
Advanced APIs are grouped under the trevrpc.Advanced namespace value. NewRawQUICClient, DialRawWebTransport, and NewRawWebTransportClient create raw single-connection transports. trevrpc.Advanced.Channel(channel).AddPath operates on the current native QUIC generation, and RawWebTransportSession exposes the current WebTransport session. Raw transports do not reconnect.
Where TrevRPC controls QUIC setup, 0-RTT application data is disabled. TLS resumption may shorten a reconnect handshake but never changes RPC semantics.
Use per-call options to override client defaults:
response, err := client.SayHello(
ctx,
&greeter.HelloRequest{Name: "TrevRPC"},
trevrpc.WithMetadata("authorization", []byte("Bearer trevrpc-example-token")),
)
Client Options
trevrpc.CallOption controls per-call behavior.
| Option | Default |
|---|---|
| Timeout | None |
| Max unary response body | 4 MiB |
| Max response messages | 4096 |
| Max cumulative response stream body | 16 MiB |
| Stream idle timeout | 30 seconds |
| Metadata | Empty |
Important option helpers:
WithTimeoutandWithoutTimeout.WithMaxResponseBodySize.WithMaxResponseMessagesandWithoutMaxResponseMessages.WithMaxResponseStreamBodySizeandWithoutMaxResponseStreamBodySize.WithStreamIdleTimeoutandWithoutStreamIdleTimeout.WithMetadataandWithMetadataMap.
Generated clients merge constructor-level options first, then method-level options.
Server Options
trevrpc.ServerOptions controls server limits.
| Option | Default |
|---|---|
| Max frame size | 4 MiB |
| Max concurrent connections | 256 |
| Max concurrent streams per connection | 64 |
| Max concurrent requests | 1024 |
| Graceful shutdown timeout | 30 seconds |
| Initial request timeout | 10 seconds |
| Max stream messages | 4096 |
| Max cumulative stream body | 16 MiB |
| Stream idle timeout | 30 seconds |
| Ordinary HTTP/3 enabled | false |
| HTTP/3 path | /trevrpc |
| HTTP/3 admission | Nil; otherwise-valid requests accepted |
| WebTransport enabled | false |
| WebTransport admission | Nil; WebTransport sessions are rejected |
Set options with server.SetOptions(options).
Streams
Go streams implement:
type MessageStream[T any] interface {
Recv() (T, error)
Close() error
}
Generated client-streaming and bidirectional-streaming clients return call objects with Send; client-streaming calls finish with CloseAndRecv, and bidirectional calls use Recv plus CloseSend. For fixed request streams, generated FromStream helpers avoid the interactive send path.
Helpful constructors and adapters:
EmptyStream[T]()creates an empty stream.FromSlice(values...)creates a stream from in-memory values.SingleMessageStream(value)creates a one-message stream.EncodeStream(stream)converts protobuf messages into byte bodies.DecodeStream(stream, newMessage)converts byte bodies into protobuf messages.StatusStream(status)creates a stream that returns one status error.
Statuses and Errors
Go uses *trevrpc.Status, which implements error. Status codes match canonical gRPC numeric values.
Use constructors such as:
trevrpc.InvalidArgument("message").trevrpc.DeadlineExceeded("message").trevrpc.ResourceExhausted("message").trevrpc.Unauthenticated("message").trevrpc.Unimplemented("message").
trevrpc.StatusFromError preserves *trevrpc.Status, maps frame-too-large errors to ResourceExhausted, and maps generic errors to Internal.
Observability
Server metrics use the trevrpc.Metrics interface:
type Metrics interface {
RPCStarted(RPCStarted)
RPCFinished(RPCFinished)
}
Metrics callbacks run inline on the RPC task and should not block.