To expose an endpoint as an finatra server, first add the following dependency:
"com.softwaremill.sttp.tapir" %% "tapir-finatra-server" % "0.13.2"
and import the package:
import sttp.tapir.server.finatra._
or if you would like to use cats-effect project, you can add the following dependency:
"com.softwaremill.sttp.tapir" %% "tapir-finatra-server-cats" % "0.13.2"
and import the packate:
import sttp.tapir.server.finatra.cats._
This adds extension methods to the Endpoint
type: toRoute
and toRouteRecoverErrors
. The first one
requires the logic of the endpoint to be given as a function of type (note this is a Twitter Future
or a cats-effect project's Effect
type):
I => Future[Either[E, O]]
or
An effect type that supports cats-effect's Effect[F]
type, for example, project Catbird's Rerunnbale
.
I => Rerunnable[Either[E, O]]
The second 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]
or type I => F[O]
if F
supports cat-effect's Effect
type.
For example:
import sttp.tapir._
import sttp.tapir.server.finatra._
import com.twitter.util.Future
def countCharacters(s: String): Future[Either[Unit, Int]] =
Future.value(Right[Unit, Int](s.length))
val countCharactersEndpoint: Endpoint[String, Unit, Int, Nothing] =
endpoint.in(stringBody).out(plainBody[Int])
val countCharactersRoute: FinatraRoute = countCharactersEndpoint.toRoute(countCharacters)
or a cats-effect's example:
import cats.effect.IO
import sttp.tapir._
import sttp.tapir.server.finatra.cats._
def countCharacters(s: String): IO[Either[Unit, Int]] =
IO.pure(Right[Unit, Int](s.length))
val countCharactersEndpoint: Endpoint[String, Unit, Int, Nothing] =
endpoint.in(stringBody).out(plainBody[Int])
val countCharactersRoute: FinatraRoute = 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: FinatraRoute = anEndpoint.toRoute((logic _).tupled)
Now that you've created the FinatraRoute
, add TapirController
as a trait to your Controller
. You can then
add the created route with addTapirRoute
.
class MyController extends Controller with TapirController {
addTapirRoute(endpoint.toRoute { (s: String, i: Int) => ??? })
}