8 Protobuf and Code Generation
trev edited this page 2026-07-12 19:35:14 -04:00

Protobuf and Code Generation

TrevRPC follows the same basic workflow as ConnectRPC: protobuf defines the API, and code generation creates type-safe client and server glue. Application code implements the generated service trait or interface.

Example Service

syntax = "proto3";

package example.greeter;

service Greeter {
  rpc SayHello(HelloRequest) returns (HelloReply);
  rpc LotsOfReplies(HelloRequest) returns (stream HelloReply);
  rpc LotsOfGreetings(stream HelloRequest) returns (HelloReply);
  rpc BidiHello(stream HelloRequest) returns (stream HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

This schema covers all supported RPC shapes:

Protobuf shape TrevRPC kind
Req -> Res Unary
Req -> stream Res Server streaming
stream Req -> Res Client streaming
stream Req -> stream Res Bidirectional streaming

Install Generators Locally

Install the Rust generator from the repository root:

cargo install --path trevrpc-rust/crates/protoc-gen-trevrpc-rust

Install the Go generator from the Go module directory:

cd trevrpc-go
go install ./cmd/protoc-gen-trevrpc-go

Install the C generator from the repository root:

cmake -S trevrpc-c -B build/trevrpc-c-codegen \
  -DTREVRPC_BUILD_MSQUIC=OFF \
  -DTREVRPC_BUILD_WEBTRANSPORT=OFF \
  -DTREVRPC_BUILD_RUNTIME=OFF \
  -DTREVRPC_BUILD_TESTS=OFF
cmake --build build/trevrpc-c-codegen --target protoc-gen-trevrpc-c
cmake --install build/trevrpc-c-codegen --prefix ~/.local

Build and install the C++ runtime and protoc-gen-trevrpc-cpp from the repository root:

cmake -S trevrpc-cpp -B build/trevrpc-cpp
cmake --build build/trevrpc-cpp
cmake --install build/trevrpc-cpp --prefix ~/.local

For C++, run both protobuf's built-in C++ generator and the TrevRPC plugin against the same schema:

protoc -I proto \
  --cpp_out=gen/cpp \
  --trevrpc-cpp_out=gen/cpp \
  proto/example/greeter.proto

The plugin emits .trevrpc.hpp and .trevrpc.cpp. Compile them with protobuf's .pb.h and .pb.cc. Use normal protobuf C++ generation for the full runtime, or set option optimize_for = LITE_RUNTIME; and link protobuf lite. Do not generate protobuf-c files for the C++ runtime.

Install the JavaScript generator from the JavaScript package directory:

cd trevrpc-js
npm install
npm link

For local development without linking, call the checked-in bin/protoc-gen-trevrpc-js.js executable directly from your Buf or protoc configuration.

Build the Kotlin generator from the Kotlin project:

cd trevrpc-kotlin
./gradlew :protoc-gen-trevrpc-kotlin:installDist

The executable is under protoc-gen-trevrpc-kotlin/build/install/protoc-gen-trevrpc-kotlin/bin/. Add that directory to PATH or reference the executable directly.

Buf Configuration

Example buf.gen.yaml:

version: v2
plugins:
  - local: protoc-gen-trevrpc-rust
    out: trevrpc-rust/src/generated
    opt:
      - runtime_path=::trevrpc
      - package_root=crate
  - local: protoc-gen-trevrpc-go
    out: trevrpc-go/generated
    opt:
      - runtime_import=trev.zip/llc/trevrpc/trevrpc-go
  - local: protoc-gen-trevrpc-c
    out: trevrpc-c/generated
    opt:
      - runtime_include=trevrpc.h
  - local: protoc-gen-trevrpc-cpp
    out: trevrpc-cpp/generated
  - local: protoc-gen-trevrpc-kotlin
    out: trevrpc-kotlin/generated
    opt:
      - runtime_package=zip.trev.trevrpc
  - local: protoc-gen-trevrpc-js
    out: trevrpc-js/generated
    opt:
      - runtime_import=trevrpc-js

The exact output directories depend on your project layout. Generated TrevRPC files should be compiled alongside the protobuf message types generated by your protobuf generator.

Rust Generator

The Rust plugin is protoc-gen-trevrpc-rust.

It generates:

  • An async service trait per protobuf service.
  • A {Service}Client<T> generic over a TrevRPC transport.
  • A register_{service} function that installs routes on trevrpc::server::Server.

Supported options:

Option Default Purpose
runtime_path ::trevrpc Path used to refer to the TrevRPC runtime in generated code
file_suffix .trevrpc.rs Suffix for generated files
package_root crate Root path used when generated code references messages from other protobuf packages

The Rust generator emits one file per protobuf package. A package named hello.v1 produces hello.v1.trevrpc.rs by default.

Include generated TrevRPC bindings in the same Rust module as the corresponding prost message types:

pub mod hello {
    pub mod v1 {
        include!(concat!(env!("OUT_DIR"), "/hello.v1.rs"));
        include!(concat!(env!("OUT_DIR"), "/hello.v1.trevrpc.rs"));
    }
}

Generated Rust service signatures use concrete protobuf message types and trevrpc::BoxMessageStream<T> for streamed inputs and outputs.

Go Generator

The Go plugin is protoc-gen-trevrpc-go.

It generates:

  • A {Service}Server interface.
  • A {Service}Client with typed methods.
  • A New{Service}Client constructor.
  • A Register{Service}Server function that installs routes on *trevrpc.Server.

Supported options:

Option Default Purpose
runtime_import trev.zip/llc/trevrpc/trevrpc-go Import path for the TrevRPC Go runtime
file_suffix .trevrpc.go Suffix for generated files

The Go generator emits output only for requested proto files that contain services. A file named greeter.proto produces greeter.trevrpc.go by default.

Generated Go service signatures use protobuf message pointers and trevrpc.MessageStream[T] for streamed inputs and outputs.

C Generator

The C plugin is protoc-gen-trevrpc-c.

It generates:

  • A service implementation struct with one function pointer per RPC.
  • Typed unary, server-streaming, client-streaming, and bidirectional-streaming client helpers.
  • Typed stream send and receive helpers around trevrpc_stream.
  • A registration function that installs protobuf-c decoding/encoding adapters on trevrpc_server.

Supported options:

Option Default Purpose
runtime_include trevrpc.h Header path for the TrevRPC C runtime
header_suffix .trevrpc.h Suffix for generated C headers
source_suffix .trevrpc.c Suffix for generated C source files

Generated C files should be compiled with the corresponding protobuf-c output for the same .proto file. Generated handlers use protobuf-c message pointers and trevrpc_stream* for streamed inputs and outputs.

C++ Generator

The C++ plugin is protoc-gen-trevrpc-cpp. It generates:

  • A synchronous {Service}Service interface for server implementations.
  • A synchronous {Service}Client with typed operations for all four RPC shapes.
  • A Register{Service} function that installs the implementation on Server.
  • Explicit Result return values for calls, stream operations, registration, and lifecycle operations that can fail.
  • Serialization adapters for protobuf C++ full and lite generated message classes.

The C++ generator consumes protobuf descriptors but does not generate message classes itself. Generate messages with protobuf's C++ generator and compile those files beside the TrevRPC service bindings. A schema's protobuf full-versus-lite selection determines the message runtime; the TrevRPC binding shape remains the same.

Supported options:

Option Default Purpose
runtime_include trevrpc/trevrpc.hpp C++ runtime header used by generated code
header_suffix .trevrpc.hpp Generated binding header suffix
source_suffix .trevrpc.cpp Generated binding source suffix

The C++ layer does not use protobuf-c. Generated adapters serialize protobuf C++ messages to bytes, pass those bytes through the existing trevrpc-c transport and server primitives, and parse response bytes back into protobuf C++ messages. This keeps the C transport responsible for QUIC, framing, limits, cancellation, and channel reconnection while C++ owns typed protobuf integration.

Generated declarations use the protobuf package's C++ namespace and concrete protobuf message types. Clients own a shared Channel; registration retains a shared service implementation through the server route adapters.

Kotlin Generator

The Kotlin plugin is protoc-gen-trevrpc-kotlin.

It generates:

  • A {Service}Service interface with suspend handlers and Flow streams.
  • A {Service}Client with typed convenience and interactive streaming methods.
  • A register{Service} function that installs routes on zip.trev.trevrpc.Server.
  • Protobuf Java message codecs plus service, method, and RPC-kind constants.

Supported options:

Option Default Purpose
runtime_package zip.trev.trevrpc Package used to refer to the Kotlin core runtime
file_suffix .trevrpc.kt Suffix for generated Kotlin binding files

The generator emits output only for requested proto files that contain services. A file named greeter.proto produces greeter.trevrpc.kt by default. Generated bindings use protobuf Java message classes, so compile them alongside Java or Kotlin protobuf output for the same schema.

Streaming convenience methods use kotlinx.coroutines.flow.Flow. Client-streaming and bidirectional methods also expose call objects with bounded send, closeSend, receive, and close operations.

JavaScript Generator

The JavaScript plugin is protoc-gen-trevrpc-js.

It generates:

  • A {Service}Client class with typed TrevRPC method names.
  • A create{Service}Client helper.
  • A {Service}Service descriptor used by the runtime client factory.
  • A protobuf.js reflection root embedded in the generated file.
  • A companion TypeScript declaration file for the generated JavaScript module.

Supported options:

Option Default Purpose
runtime_import trevrpc-js Import path for the TrevRPC JavaScript runtime
file_suffix .trevrpc.js Suffix for generated files

The JavaScript generator emits output only for requested proto files that contain services. A file named greeter.proto produces greeter.trevrpc.js and greeter.trevrpc.d.ts by default.

Generated JavaScript clients use protobuf.js reflection types and accept plain JavaScript objects for protobuf messages. Unary calls resolve to one response object. Server-streaming calls resolve to an async iterable of response objects. Client-streaming and bidirectional-streaming calls resolve to sendable call objects.

Generated declaration files include TypeScript interfaces for the protobuf message types embedded in the generated reflection root. TypeScript projects import the same .trevrpc.js module path and receive the companion .d.ts types:

import { GreeterClient } from "./hello/v1/greeter.trevrpc.js";
import type { HelloRequest } from "./hello/v1/greeter.trevrpc.js";

const request: HelloRequest = { name: "TrevRPC" };

What Generated Code Does

Generated client methods call the runtime helpers for the matching RPC kind:

  • Rust: trevrpc::client::unary, server_streaming, client_streaming, bidirectional_streaming.
  • Go: trevrpc.Unary, ServerStreaming, ClientStreaming, BidirectionalStreaming.
  • C: trevrpc_client_call_unary, trevrpc_client_start_stream, and generated typed stream helpers.
  • C++: synchronous generated client and stream operations that serialize protobuf C++ messages and delegate byte transport to trevrpc-c.
  • Kotlin: Client.unaryEnvelope, serverStreaming, clientStreaming, and bidirectionalStreaming from zip.trev.trevrpc.
  • JavaScript: unary, serverStreaming, clientStreaming, bidirectionalStreaming from trevrpc-js.

Generated Rust, Go, C, C++, and Kotlin registration functions decode request protobuf messages, call your implementation, encode response protobuf messages, and register the method with the runtime server. C++ performs that work with protobuf C++ full or lite APIs rather than protobuf-c.

Error Handling in Generated Code

Handlers return regular language errors that the runtime maps into TrevRPC statuses:

  • Rust service methods return Result<_, trevrpc::Status>.
  • Go service methods return (_, error), where *trevrpc.Status preserves an explicit status code.
  • C service methods return 0 on success or a TrevRPC/POSIX-style negative error code that the runtime maps into a status.
  • C++ service methods and client operations return explicit Result values. Streaming send, receive, finish, and terminal status failures are surfaced at the operation that observes them.
  • Kotlin service methods return message values or Flow and throw TrevRpcException to preserve an explicit status code.

JavaScript generated clients throw TrevRpcError for non-OK TrevRPC statuses and mapped transport failures.

Decode failures are reported as InvalidArgument. Unknown methods are reported as Unimplemented. Oversized frames and streams are reported as ResourceExhausted.