1

Trying to compile this small ZIO friendly Tapir/Http4s Endpoint description

import io.circe.generic.auto._
import org.http4s.HttpRoutes
import sttp.tapir.json.circe
import sttp.tapir.ztapir._
import sttp.tapir.server.http4s.ztapir._
import sttp.tapir.endpoint
import zio.RIO
import zio.interop.catz._

case class HealthReplyDTO(message: String)

final class HealthEndpointZTapir[E]() {
  private val prefixPath = "/health"
  val healthOkReply = HealthReplyDTO("OK")

  private val routeDescription: ZEndpoint[Unit, Unit, HealthReplyDTO] =
    endpoint.get.in(prefixPath).out(circe.jsonBody[HealthReplyDTO]).description("Health Endpoint")

  val route: HttpRoutes[RIO[E, *]]
  = routeDescription.toRoutes( _ => RIO.succeed(healthOkReply))

}

and keep getting this on the last line.

Type mismatch. Required: HttpRoutes[RIO[E, *]], found: HttpRoutes[zio.Task]

A Task is a sub type of RIO so this should work fine right? Or am I missing something here. A bit of a noob to this world, so some help would be much appreciated.

arinray
  • 178
  • 1
  • 12

2 Answers2

3

Task is indeed a subtype of RIO[R, *], but HttpRoutes from http4s is invariant, and hence the error message.

The .toRoutes method returns HttpRoutes[Task] as a consequence of integrating with http4s, which doesn't have typed errors. In general, http4s can throw any type of exceptions when setting up the server and handling the routes, so we need to work with Task.

adamw
  • 8,038
  • 4
  • 28
  • 32
  • Hi, while this explains the reason for the compilation error, it doesn't reach me to a solution. I'm asking peepz at Http4s to see if they can help bridge the gap. – arinray Oct 01 '20 at 08:44
  • Does you explanation mean that creating an HTTPRoutes[RIO[E, *]] is not possible using tapir/ztapir? – arinray Oct 01 '20 at 09:56
0

I finally ended up doing something like this. The compiler needs a bit of help in the last line, where implicits proved to be not enough. The ztapir part of tapir is fixed in ZIO effect type Task so that was not usable in my case

import io.circe.generic.auto._
import org.http4s.HttpRoutes
import sttp.tapir.json.circe
import sttp.tapir.server.http4s._
import sttp.tapir.endpoint
import zio.RIO
import zio.interop.catz._
import sttp.tapir._

final class HealthEndpointTapir[E]() extends TapirHttp4sServer {

  private val prefixPath = "/health"
  val healthOkReply: HealthReplyDTO = HealthReplyDTO("OK")
  implicit val customServerOptions: Http4sServerOptions[RIO[E, *]] = Http4sServerOptions
    .default[RIO[E, *]]

  private val routeDescription: Endpoint[Unit, Unit, HealthReplyDTO, Any] =
    endpoint.get.in(prefixPath).out(circe.jsonBody[HealthReplyDTO]).description("Health Endpoint")

  val route: HttpRoutes[RIO[E, *]]
  = RichHttp4sHttpEndpoint[Unit, Unit, HealthReplyDTO, RIO[E, *]
(routeDescription).toRoutes( _ => RIO.succeed(Right(healthOkReply)))

}
arinray
  • 178
  • 1
  • 12