Serving gRPC from Play

To be able to serve gRPC from a Play Framework app you must enable HTTP/2 Support with HTTPS and the ALPN agent. This is still somewhat involved and we hope to simplify it.

Warning

To use gRPC in Play Framework you must enable HTTP/2 Support.

Generating classes from the gRPC service definition is done buy adding the Akka gRPC plugin to your sbt build along with the Play gRPC generators:

sbt
// in project/plugins.sbt:
addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "0.7.0")
libraryDependencies += "com.lightbend.play" %% "play-grpc-generators" % "0.7.1"

Then you need to enable the Play server side code generator in build.sbt:

Scala
enablePlugins(AkkaGrpcPlugin)
import play.grpc.gen.scaladsl.PlayScalaServerCodeGenerator
akkaGrpcExtraGenerators += PlayScalaServerCodeGenerator
libraryDependencies += "com.lightbend.play" %% "play-grpc-runtime" % "0.7.1"
Java
enablePlugins(AkkaGrpcPlugin)
import play.grpc.gen.javadsl.PlayJavaServerCodeGenerator
akkaGrpcExtraGenerators += PlayJavaServerCodeGenerator
libraryDependencies += "com.lightbend.play" %% "play-grpc-runtime" % "0.7.1"

The plugin will look for .proto service descriptors in app/protobuf and output an abstract class per service that you then implement, so for example for the following protobuf descriptor:

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;
}

You will get an abstract class named example.myapp.helloworld.grpc.helloworld.AbstractGreeterServiceRouter example.myapp.helloworld.grpc.AbstractGreeterServiceRouterCreate a concrete subclass implementing this wherever you see fit in your project, let’s say controller.GreeterServiceImpl like so:

Scala
package controllers

import akka.actor.ActorSystem
import akka.stream.Materializer
import example.myapp.helloworld.grpc.helloworld.AbstractGreeterServiceRouter
import example.myapp.helloworld.grpc.helloworld.HelloReply
import example.myapp.helloworld.grpc.helloworld.HelloRequest
import javax.inject.Inject
import javax.inject.Singleton

import scala.concurrent.Future

/** User implementation, with support for dependency injection etc */
@Singleton
class GreeterServiceImpl @Inject()(implicit mat: Materializer, actorSystem: ActorSystem)
    extends AbstractGreeterServiceRouter(mat, actorSystem) {

  override def sayHello(in: HelloRequest): Future[HelloReply] = Future.successful(HelloReply(s"Hello, ${in.name}!"))

}
Java
package controllers;

import akka.actor.ActorSystem;
import akka.stream.Materializer;
import com.google.inject.Inject;
import example.myapp.helloworld.grpc.AbstractGreeterServiceRouter;
import example.myapp.helloworld.grpc.HelloReply;
import example.myapp.helloworld.grpc.HelloRequest;

import javax.inject.Singleton;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

/** User implementation, with support for dependency injection etc */
@Singleton
public class GreeterServiceImpl extends AbstractGreeterServiceRouter {

  @Inject
  public GreeterServiceImpl(Materializer mat, ActorSystem actorSystem) {
    super(mat, actorSystem);
  }

  @Override
  public CompletionStage<HelloReply> sayHello(HelloRequest in) {
    String message = String.format("Hello, %s!", in.getName());
    HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
    return CompletableFuture.completedFuture(reply);
  }
}

And then add the router to your Play conf/routes file. Note that the router already knows its own path since it is based on the package name and service name of the service and therefore the path / is enough to get it to end up in the right place (in this example the path will be /helloworld.GreeterService). It cannot be added at an arbitrary path (if you try to do so an exception will be thrown when the router is started).

->     /   controllers.GreeterServiceImpl

A gRPC client can now connect to the server and call the provided services.

Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.