Compilation problem between HttpRoutes[RIO[E, *]] and HttpRoutes[zio.Task]

408 views Asked by At

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.

2

There are 2 answers

2
adamw On

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.

0
arinray On

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)))

}