Running as an akka-http server¶
To expose an endpoint as an akka-http server, first add the following dependency:
"com.softwaremill.tapir" %% "tapir-akka-http-server" % "0.5"
and import the package:
import tapir.server.akkahttp._
This adds extension methods to the Endpoint
type: toDirective
, toRoute
and toRouteRecoverErrors
. The first two
require the logic of the endpoint to be given as a function of type:
I => Future[Either[E, O]]
The third recovers errors from failed futures, and hence requires that E
is a subclass of Throwable
(an exception);
it expects a function of type I => Future[O]
.
For example:
import tapir._
import tapir.server.akkahttp._
import scala.concurrent.Future
import akka.http.scaladsl.server.Route
def countCharacters(s: String): Future[Either[Unit, Int]] =
Future.successful(Right[Unit, Int](s.length))
val countCharactersEndpoint: Endpoint[String, Unit, Int, Nothing] =
endpoint.in(stringBody).out(plainBody[Int])
val countCharactersRoute: Route = countCharactersEndpoint.toRoute(countCharacters)
Note that these functions take one argument, which is a tuple of type I
. This means that functions which take multiple
arguments need to be converted to a function using a single argument using .tupled
:
def logic(s: String, i: Int): Future[Either[Unit, String]] = ???
val anEndpoint: Endpoint[(String, Int), Unit, String, Nothing] = ???
val aRoute: Route = anEndpoint.toRoute((logic _).tupled)
The created Route
/Directive
can then be further combined with other akka-http directives, for example nested within
other routes. The tapir-generated Route
/Directive
captures from the request only what is described by the endpoint.
It’s completely feasible that some part of the input is read using akka-http directives, and the rest using tapir endpoint descriptions; or, that the tapir-generated route is wrapped in e.g. a metrics route. Moreover, “edge-case endpoints”, which require some special logic not expressible using tapir, can be always implemented directly using akka-http. For example:
val myRoute: Route = metricsDirective {
securityDirective { user =>
tapirEndpoint.toRoute(input => /* here we can use both `user` and `input` values */)
}
}
Streaming¶
The akka-http interpreter accepts streaming bodies of type Source[ByteString, Any]
, which can be used both for sending
response bodies and reading request bodies. Usage: streamBody[Source[ByteString, Any]](schema, mediaType)
.
Configuration¶
The interpreter can be configured by providing an implicit AkkaHttpServerOptions
value and status mappers, see
common server configuration for details.