Play framework
Client and server backed by Play framework.
Client
"org.julienrf" %% "endpoints-play-client" % "0.15.0"
The Endpoints
interpreter fixes the Endpoint[A, B]
type to a function from A
to Future[B]
:
type Endpoint[A, B] = A => Future[B]
This means that, given the following endpoint definition:
val someResource: Endpoint[Int, String] =
endpoint(get(path / "some-resource" / segment[Int]()), ok(textResponse))
It can be invoked as follows:
val eventuallyString: Future[String] = someResource(42)
Server
"org.julienrf" %% "endpoints-play-server" % "0.15.0"
Endpoints
The Endpoints
interpreter provides a routesFromEndpoints
operation that turns a sequence of endpoints with their implementation into a play.api.routing.Router.Routes
value that can be integrated to your Play application.
For instance, given the following endpoint definition:
val someResource: Endpoint[Int, String] =
endpoint(get(path / "some-resource" / segment[Int]()), ok(textResponse))
It can be implemented as follows:
val routes: Router.Routes =
routesFromEndpoints(
someResource.implementedBy(x => s"Received $x")
)
In practice, the routes are put in a class taking an endpoints.play.server.PlayComponents
parameter. An HTTP server can then be started as in the following example:
object Main {
// JVM entry point that starts the HTTP server
def main(args: Array[String]): Unit = {
val playConfig = ServerConfig(port =
sys.props.get("http.port").map(_.toInt).orElse(Some(9000))
)
NettyServer.fromRouterWithComponents(playConfig) { components =>
val playComponents = PlayComponents.fromBuiltInComponents(components)
new CounterServer(playComponents).routes orElse new DocumentationServer(
playComponents
).routes
}
}
}
ChunkedEntities
The ChunkedEntities
interpreter fixes the type Chunks[A]
to akka.stream.scaladsl.Source[A, _]
.
For instance, given the following chunked endpoint definition:
val logo: Endpoint[Unit, Chunks[Array[Byte]]] =
endpoint(get(path / "logo.png"), ok(bytesChunksResponse))
It can be implemented as follows:
import akka.stream.scaladsl.FileIO
import java.nio.file.Paths
val logoHandler =
logo.implementedBy { _ =>
FileIO.fromPath(Paths.get("/foo/bar/logo.png")).map(_.toArray)
}
Error handling
When the server processes requests, three kinds of errors can happen: the incoming request doesn’t match any endpoint, the request does match an endpoint but is invalid (e.g. one parameter has a wrong type), or an exception is thrown.
The incoming request doesn’t match any endpoint
In that case, the router constructed by endpoints can’t do anything. You have to deal with such errors in the usual Play way: by using a custom play.api.http.HttpErrorHandler
.
The incoming request is invalid
In that case, endpoints returns a “Bad Request” (400) response reporting all the errors in a JSON array. You can change this behavior by overriding the handleClientErrors method.
An exception is thrown
If an exception is thrown during request decoding, or when running the business logic, or when encoding the response, endpoints returns an “Internal Server Error” (500) response reporting the error in a JSON array. You can change this behavior by overriding the handleServerError method.