Skip to content

Add kotlinx-rpc gRPC integration#507

Merged
andrewparmet merged 31 commits intoopen-toast:mainfrom
andrewparmet:ap/grpc-krpc
May 8, 2026
Merged

Add kotlinx-rpc gRPC integration#507
andrewparmet merged 31 commits intoopen-toast:mainfrom
andrewparmet:ap/grpc-krpc

Conversation

@andrewparmet
Copy link
Copy Markdown
Collaborator

@andrewparmet andrewparmet commented Apr 12, 2026

  • Add grpcKrpc generation flag that emits @Grpc-annotated Kotlin interfaces from proto service definitions
  • The kotlinx-rpc compiler plugin processes these interfaces at compile time, synthesizing stubs, service descriptors, and method descriptors
  • New protokt-runtime-grpc-krpc published module: marshaller bridge between protokt serialization and kotlinx-rpc's GrpcMarshaller interface
  • Working HelloWorld example with passing multiplatform test (JVM + native) using kotlinx-rpc GrpcServer/GrpcClient
  • Third-party variant for vendored common protos (proto-google-common-protos-grpc-krpc)
  • Remove Generate.all() convenience method (misleading with multiple mutually exclusive gRPC backends)
  • Add Generate.grpcKrpcLite() and Generate.grpcKotlin() convenience methods
  • Replace @OnlyForUseByGeneratedProtoCode on AbstractDeserializer/AbstractMessage with @SubclassOptInRequired so users don't need opt-in to use generated companions as Deserializer<T>

Standalone example

The kRPC example lives in standalone-examples/grpc-krpc/ as its own Gradle build (mirroring gradle-plugin-integration-test/). This is necessary because the kotlinx-rpc compiler plugin resolves as {kotlinVersion}-{rpcVersion}, permanently coupling its Kotlin version to kRPC's release cadence and we don't want to be bound to that cadence.

What protokt generates (from hello_world.proto):

@Grpc(protoPackage = "helloworld")
public interface Greeter {
  public suspend fun SayHello(message: HelloRequest): HelloReply
}

Streaming methods (from route_guide.proto):

@Grpc(protoPackage = "io.grpc.examples.routeguide")
public interface RouteGuide {
  public suspend fun GetFeature(message: Point): Feature              // unary
  public fun ListFeatures(message: Rectangle): Flow<Feature>          // server streaming
  public suspend fun RecordRoute(message: Flow<Point>): RouteSummary  // client streaming
  public fun RouteChat(message: Flow<RouteNote>): Flow<RouteNote>     // bidi
}

What the kotlinx-rpc compiler plugin generates from that:

  • Greeter$$rpcServiceStub: implements Greeter, delegates calls through RpcClient
  • Greeter$$rpcServiceStub$Companion: implements both RpcServiceDescriptor<Greeter> and GrpcServiceDescriptor<Greeter>, provides delegate(marshallerResolver, config) for wiring method descriptors and marshallers

User-facing API:

// server
val server =
    GrpcServer(port) {
        messageMarshallerResolver = resolver
        services {
            registerService<Greeter> { GreeterService() }
        }
    }
server.start()

// client
val client =
    GrpcClient("localhost", port) {
        messageMarshallerResolver = resolver
        credentials = plaintext()
    }
val greeter = client.withService<Greeter>()
val reply = greeter.SayHello(HelloRequest { name = "world" })

@andrewparmet andrewparmet changed the title Ap/grpc krpc Add kotlinx-rpc gRPC integration Apr 12, 2026
…g example)

- Add `grpcKrpc` generation flag plumbed through ProtoktExtension, PluginConfig,
  ProtobufBuild, PluginParams, GeneratorContext, and FileGenerator
- Add GrpcKrpcServiceGenerator: emits @Grpc-annotated interfaces from proto
  service definitions for processing by the kotlinx-rpc compiler plugin
- Add ProtoktGrpcMarshaller: zero-copy marshaller bridge using kotlinx-io
  (Message.serialize(Sink) / Deserializer.deserialize(Source))
- Add examples/grpc-krpc with HelloWorld server, client, and passing test
  using kotlinx-rpc GrpcServer/GrpcClient with protokt messages
- Add third-party/proto-google-common-protos-grpc-krpc variant
- Add kotlinx-rpc version catalog entries and JetBrains Space plugin repo
- Move kotlinx-rpc Gradle plugin to buildSrc classpath via version catalog
- Remove pluginManagement block from settings.gradle.kts (no longer needed)
- Remove buildscript block from example (plugin resolved via buildSrc)
- Add API dump for proto-google-common-protos-grpc-krpc
Convert protokt-runtime-grpc-krpc and proto-google-common-protos-grpc-krpc
from JVM-only to KMP with JVM + native (all targets except mingwX64, no JS,
matching kotlinx-rpc's published target set). Generate @grpc interfaces only
on the primary target to prevent redeclarations across native compilations.
Add OnlyForUseByGeneratedProtoCode opt-in to the example.
…rpc-188

- Convert grpc-krpc example from JVM-only to multiplatform with native
  targets (macOS/Linux host target), running end-to-end gRPC tests on
  both JVM and native
- Upgrade kotlinx-rpc from 0.11.0-grpc-187 to 0.11.0-grpc-188 which
  has native gRPC support built in (no fork needed)
- Pin Kotlin to 2.3.20 (kRPC compiler plugin not yet published for
  2.3.21)
- Remove @OnlyForUseByGeneratedProtoCode from AbstractDeserializer and
  AbstractMessage: the annotation leaked to consumers using generated
  companion objects as Deserializer<T>
- Extract configureMultiplatformJvm() helper to share JVM setup between
  protokt-runtime-grpc-krpc and the example
…nce methods

- New protokt.krpc-conventions precompiled plugin: bundles
  kotlin-multiplatform + java-base + JVM config + Space repo + rpc
  opt-in
- Add Generate.grpcKrpc(): types + descriptors + kRPC interfaces
- Add Generate.grpcKotlin(): types + descriptors + gRPC descriptors +
  Kotlin stubs
- Replace @OnlyForUseByGeneratedProtoCode on AbstractDeserializer and
  AbstractMessage with @SubclassOptInRequired: users can freely use
  generated messages and deserializers but subclassing still requires
  opt-in
The kotlinx-rpc compiler plugin resolves as {kotlinVersion}-{rpcVersion}
which permanently couples its Kotlin version to kRPC's release cadence.
Move the example to a standalone Gradle project (standalone-examples/)
with its own pinned Kotlin version so the main build can track latest
Kotlin independently.

- standalone-examples/grpc-krpc: self-contained build consuming protokt
  from the integration repo, pinned to Kotlin 2.3.20
- Main build reverted to Kotlin 2.3.21
- CI runs the standalone example after publishToIntegrationRepository
- Remove grpcKrpc() convenience method (service descriptors conflict
  with @grpc interface names); grpcKrpcLite() remains
- Use libs.versions.toml instead of gradle.properties for version
  declarations
- Fix broken README link to examples/grpc-krpc (now standalone-examples)
- Fix CI step: remove dead protoktVersion computation (build reads from
  filesystem directly)
- Fix grpcKrpc KDoc: reference client/server deps, not grpc-core
- Remove hardcoded version strings from build.gradle.kts
@andrewparmet andrewparmet marked this pull request as ready for review May 8, 2026 11:52
@andrewparmet andrewparmet requested a review from ogolberg May 8, 2026 11:52
@andrewparmet andrewparmet merged commit 6ce71fc into open-toast:main May 8, 2026
29 of 50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants