gRPC in Kotlin: when to use and how not to make a mistake

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

gRPC in Kotlin: when to use it so you don't regret it



Imagine: you are writing microservices in Kotlin, everything works, but requests between them become sluggish. The REST API you loved so much starts to annoy you — too much serialization, too much HTTP overhead. You start googling "fast way to communicate between microservices" and stumble upon gRPC. And immediately the question arises: "Isn't this overkill? Maybe leave it as is?"



Let's figure it out. gRPC in Kotlin is not just "another protocol". It is a completely different approach to inter-service communication. It is faster, stricter, and more powerful than REST, but also more complex. In this article, I will tell you when gRPC is your best friend and when it's better to stick with good old REST. And, of course, I will show real Kotlin code.



What is gRPC and why is it so popular in the Kotlin world?



gRPC is a remote procedure call (RPC) framework from Google. It uses Protocol Buffers (protobuf) for data serialization and HTTP/2 for transport. If REST is like sending letters by mail (each request is a separate letter), then gRPC is like a direct phone call: fast, bidirectional, with minimal latency.



For Kotlin developers, gRPC is especially interesting because:



  • Kotlin coroutines pair perfectly with gRPC's asynchronous streams.
  • Strict protobuf typing means fewer runtime errors.
  • Support for bidirectional streaming — ideal for chats, games, and real-time applications.


But let's be honest: gRPC is not needed everywhere. And now I will show you how to determine if your project needs it.



When to use gRPC in Kotlin: 5 rock-solid scenarios



1. Microservices with high performance requirements



If your microservices communicate with tens of thousands of requests per second, gRPC outperforms REST by 2-5 times in speed. All thanks to the binary protobuf format and HTTP/2 multiplexing.



Example: you have a recommendation service in Kotlin that must return personalized content in milliseconds. REST with JSON will be "heavy" — each request contains field names, brackets, and other textual clutter. Protobuf transmits only numbers and strings without unnecessary wrapping.



// Definition in .proto fileservice Recommender {    rpc GetRecommendations (UserRequest) returns (RecommendationsResponse);}

message UserRequest { string user_id = 1; int32 max_results = 2;}

message RecommendationsResponse { repeated Item items = 1;}


The Kotlin client code will look concise:



val stub = RecommenderGrpc.newBlockingStub(channel)val request = UserRequest.newBuilder()    .setUserId("user_123")    .setMaxResults(10)    .build()

val response = stub.getRecommendations(request)println("Received ${response.itemsCount} recommendations")


2. Real-time data streaming



gRPC supports four types of streaming: server-side, client-side, bidirectional, and simple unary. In Kotlin, this is especially convenient with coroutines — you can stream data as a Flow.



Example: a log server that sends new records to the client in real time.



// Server-side in Kotlinclass LogServiceImpl : LogServiceGrpcKt.LogServiceCoroutineImplBase() {    override fun streamLogs(request: LogRequest): Flow<LogEntry> = flow {        val logSource = getLogSource(request.logType)        logSource.collect { logEntry ->            emit(logEntry)        }    }}

// Client-sideval stub = LogServiceGrpcKt.LogServiceCoroutineStub(channel)stub.streamLogs(LogRequest.newBuilder().setLogType("error").build()) .collect { log -> println("New log: ${log.message}") }


Try doing the same with REST and WebSocket — it will be cumbersome and slow.



3. Polyglot systems

Blogs

Book Recommendations