Posted by Deanna Garcia and Louis Wasserman - Software Developers, and Developer Advocate, James Ward
At Google, we’re investing deeply in the Kotlin language and ecosystem. Android development is now Kotlin first, our engineering teams work on language evolution through the Kotlin Foundation, and inside of Google we’re using Kotlin more and more to build our backend services. We love Kotlin for its expressiveness, safety, simple async support through coroutines, and easy bidirectional interoperability with the Java programming language.
Last year, we open sourced Kotlin support for gRPC, the open source Remote Procedure Call (RPC) framework that powers thousands of microservices at Google. We’re excited to deepen our investment in the Kotlin language with official support for Kotlin in the open source Protocol Buffers project (a.k.a. “protos”), Google’s platform-neutral, high-performance data interchange format. From a proto definition, you can use the new built-in Kotlin support in the proto compiler to generate idiomatic Kotlin Domain Specific Languages (DSLs).
For example, here’s a simple protocol buffer message representing a series of dice rolls:
message DiceSeries { message DiceRoll { int32 value = 1; // value of this roll, e.g. 2..12 string nickname = 2; // string nickname, e.g. "snake eyes" } repeated DiceRoll rolls = 1; }
In the Java language, constructing a series of dice rolls might look like this:
DiceSeries series = DiceSeries.newBuilder() .addRoll(DiceRoll.newBuilder() .setValue(5)) .addRoll(DiceRoll.newBuilder() .setValue(20) .setNickname("critical hit")) .build()
With this release, protos offer an expressive set of DSL factory methods that make this code elegant and idiomatic in Kotlin. Here is the equivalent dice roll code written using the new Kotlin proto bindings:
val series = diceSeries { rolls = listOf( diceRoll { value = 5 }, diceRoll { value = 20 nickname = "critical hit" } ) }
The Kotlin version uses Kotlin type-safe builders, which makes it concise and removes the need
to explicitly call a build
method. Note that this works with both the
proto compiler’s standard and "proto lite" modes, the latter generating smaller, higher
performance classes which are more suitable for Android.
Note: Since protos use the get
prefix for
their fields and Kotlin recognizes
that as a property, reading from a proto already works smoothly from Kotlin as if
the proto were a data class.
val totalRolls = series.rolls.map { it.value }.sum() // 5 + 20 = 25
The new Kotlin Protos work great with gRPC Kotlin providing a concise syntax for messages and services. Let's walk through a basic sample.
Here is a basic "Greeter" gRPC service proto:
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
Check out the full example proto source.
The non-blocking server can be implemented concisely, taking advantage of the Kotlin proto builders:
class HelloWorldService : GreeterCoroutineImplBase() { override suspend fun sayHello(request: HelloRequest) = helloReply { message = "hello, ${request.name}" } }
Check out the full example server source.
The non-blocking client also can take advantage of the type-safe Kotlin builders with concise syntax:
val stub = GreeterCoroutineStub(channel) val request = helloRequest { name = "world" } val response = stub.sayHello(request) println("Received: ${response.message}")
Check out the full example client source.
Those code examples really illustrate how concise the syntax is, which means less boilerplate to write and less for our brains to parse as we try to read the code. What isn't evident in these examples is how nice the experience is when writing high-performance RPC code. Thanks to static typing and the type-safe builders, you can easily code-complete your way through code that is "correct" in-that the types / properties are consistent with the protos.
The protobuf compiler (protoc) now has built-in support for generating Kotlin code. A bit of configuration is needed to tell your build tool (Maven, Gradle, etc) to do that. For Gradle builds it looks like:
protobuf { // omitted protoc and plugins config generateProtoTasks { all().forEach { // omitted plugins config it.builtins { id("kotlin") } } } }
Check out the full example Gradle build.
Give the complete gRPC Kotlin example a spin and also explore other aspects like the Android client and native client (built with GraalVM Native Image) which both use the lite protos. For an example Maven project check out the grpc-hello-world-mvn sample.
For examples that can be deployed with a couple clicks on Google Cloud Run (a fully managed serverless platform) check out: grpc-hello-world-gradle, grpc-hello-world-streaming (server push), or grpc-hello-world-bidi-streaming (bi-directional streaming).
To learn more about Kotlin protos check out these docs:
Let us know how it goes and if you have any Kotlin proto issues by filing them within the official protobuf project.
Additional thanks to Adam Cozzette, Brent Shaffer, David Jones, David Winer, Jeff Grimes, John Pampuch, and Kevin Bierhoff for their contributions to this release!