Using a gRPC client in Play

Pekko gRPC has special support to allow for seamless injection of generated clients in Play. To enable this, you need first to enable the gRPC plugin as described in the client docs and then add a source generator in build.sbt:

  • Scala

  • Java

import play.grpc.gen.scaladsl.PlayScalaClientCodeGenerator
pekkoGrpcExtraGenerators += PlayScalaClientCodeGenerator
libraryDependencies += "org.playframework" %% "play-grpc-runtime" % "0.12.x"
import play.grpc.gen.javadsl.PlayJavaClientCodeGenerator
pekkoGrpcExtraGenerators += PlayJavaClientCodeGenerator
libraryDependencies += "org.playframework" %% "play-grpc-runtime" % "0.12.x"

This will generate a Play module that provides all generated clients for injection. The module must be enabled by adding it to the enabled modules in the application.conf.

You can then put the following helloworld.proto file in app/protobuf:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "example.myapp.helloworld.grpc";
option java_outer_classname = "HelloWorldProto";

package helloworld;

service GreeterService {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

The module file is generated in example.myapp.helloworld.grpc.helloworld.PekkoGrpcClientModule, example.myapp.helloworld.grpc.PekkoGrpcClientModule, which corresponds to the default value of flat_package for Scala. Java. You can read more about this in Services.

The exact package of the module will be based on the package the proto files are generated in, configured through the java_package option in the proto-file (if there are multiple different gRPC generated clients the module will be generated in the longest package prefix shared between the clients).

To hook it into Play, in application.conf:

  • Scala

  • Java

// enable the client module
play.modules.enabled += example.myapp.helloworld.grpc.helloworld.PekkoGrpcClientModule
// enable the client module
play.modules.enabled += example.myapp.helloworld.grpc.PekkoGrpcClientModule

The clients are configured with entries under pekko.grpc.client named after the client (gRPC package name dot ServiceName), again, in application.conf:

pekko.grpc.client {
  "helloworld.GreeterService" {
    host = "example.com"
    port = 9000
    # By default we connect over TLS
    #use-tls = false
  }
}

See Client Configuration for more information on the available options. If the configuration is not present for that client and it is used by some other component, the application start will fail with an exception when injecting the client (see #271).

You can now use the client in a controller by injecting it:

  • Scala

  • Java

package controllers

import javax.inject.Inject
import javax.inject.Singleton

import scala.concurrent.ExecutionContext

import example.myapp.helloworld.grpc.helloworld.GreeterServiceClient
import example.myapp.helloworld.grpc.helloworld.HelloRequest
import play.api.mvc.AbstractController
import play.api.mvc.ControllerComponents

@Singleton
class MyController @Inject() (
    implicit greeterClient: GreeterServiceClient,
    cc: ControllerComponents,
    exec: ExecutionContext,
) extends AbstractController(cc) {

  def sayHello(name: String) = Action.async {
    greeterClient
      .sayHello(HelloRequest(name))
      .map { reply =>
        Ok(s"response: ${reply.message}")
      }
  }

}
package controllers;

import example.myapp.helloworld.grpc.GreeterServiceClient;
import example.myapp.helloworld.grpc.HelloRequest;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import javax.inject.Singleton;
import play.mvc.Controller;
import play.mvc.Result;

@Singleton
public class MyController extends Controller {

  private final GreeterServiceClient greeterServiceClient;

  @Inject
  public MyController(GreeterServiceClient greeterServiceClient) {
    this.greeterServiceClient = greeterServiceClient;
  }

  public CompletionStage<Result> sayHello(String name) {
    return greeterServiceClient
        .sayHello(HelloRequest.newBuilder().setName(name).build())
        .thenApply(response -> ok("response: " + response.getMessage()));
  };
}