Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running http4s server with ZIO Env

Tags:

scala

http4s

zio

Trying to learn using ZIO library, so I decided to create a basic web service app. Idea pretty basic, use http4s lib for server and route endpoints, print "hello world" on endpoint call.

With the help of docs and examples I found, produces code:

object Main extends ManagedApp {

  type AppEnvironment = Clock with Console with HelloRepository
  type AppTask[A] = RIO[AppEnvironment, A]

  override def run(args: List[String]): ZManaged[ZEnv, Nothing, Int] = {
    val httpApp: HttpApp[AppTask] = Router[AppTask]("/" -> helloWorldService).orNotFound

    val server = ZIO.runtime[AppEnvironment].flatMap { implicit rts =>
      BlazeServerBuilder[AppTask]
        .bindHttp(8080, "0.0.0.0")
        .withHttpApp(CORS(httpApp))
        .serve
        .compile[AppTask, AppTask, ExitCode]
        .drain
    }

    (for {
      _ <- ZManaged.environment[ZEnv] >>> server.toManaged_
    } yield ())
      .foldM(err => putStrLn(s"Execution failed with: $err").as(1).toManaged_, _ => ZManaged.succeed(0))
  }

  val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask]
  import dsl._

  val helloWorldService: HttpRoutes[AppTask] = HttpRoutes.of[AppTask] {
    case GET -> Root / "hello" / name => Ok(Repo.getHello(name))
  }
}

trait HelloRepository extends Serializable {
  val helloRepository: HelloRepository.Service[Any]
}

object HelloRepository extends Serializable {
  trait Service[R] extends Serializable {
    def getHello(name: String): ZIO[R, Nothing, String]
  }
}

object Repo extends HelloRepository.Service[HelloRepository] {
  override def getHello(name: String): ZIO[HelloRepository, Nothing, String] = ZIO.succeed(s"Hello $name")
}
  • I create router: Router[AppTask]("/" ...
  • I create server: ZIO.runtime[AppEnvironment].flatMap ...
  • Then trying to start server with ZIO enviroment, but something I am missing as this line: _ <- ZManaged.environment[ZEnv] >>> server.toManaged_ is incorected, and throws error on build: Error:(34, 39) inferred type arguments [touch.Main.AppEnvironment,Throwable,Unit] do not conform to method >>>'s type parameter bounds [R1 >: zio.ZEnv,E1,B] _ <- ZManaged.environment[ZEnv] >>> server.toManaged_

Error:(34, 39) inferred type arguments [touch.Main.AppEnvironment,Throwable,Unit] do not conform to method >>>'s type parameter bounds [R1 >: zio.ZEnv,E1,B]

Error:(34, 50) type mismatch; found : zio.ZManaged[touch.Main.AppEnvironment,Throwable,Unit] (which expands to) zio.ZManaged[zio.clock.Clock with zio.console.Console with touch.HelloRepository,Throwable,Unit] required: zio.ZManaged[R1,E1,B]

maybe someone can help me with the correct syntax? also would appriacete some explanation, or link to docs, where this is explained.

like image 680
Bublik Avatar asked Feb 27 '26 07:02

Bublik


1 Answers

I would like to explain more but I don't know where you got your code sample or what your build.sbt looks like but I happen to have some http4s code lying around so I took the liberty of adding some import statements and simplifying it a bit. You can always add back the complexity I took out.

Here's what worked for me.

/tmp/http4s/test.scala

import org.http4s.implicits._
import org.http4s.server.blaze._
import org.http4s.server.Router
import org.http4s.server.middleware.CORS

import org.http4s._
import org.http4s.dsl.Http4sDsl

import zio._
import zio.clock._
import zio.console._
import zio.interop.catz._

trait HelloRepository
{
  def getHello(name: String): ZIO[AppEnvironment, Nothing, String]
}

trait AppEnvironment extends Console with Clock
{
  val helloRepository: HelloRepository
}

object Main extends App {

  type AppTask[A] = RIO[AppEnvironment, A]

  val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask]
  import dsl._

  val httpApp: HttpApp[AppTask] = Router[AppTask](
    "/" -> HttpRoutes.of[AppTask] {
      case GET -> Root / "hello" / name => Ok( ZIO.accessM[AppEnvironment](_.helloRepository.getHello(name)) )
    }
  ).orNotFound

  val program = for {
    server <- ZIO.runtime[AppEnvironment]
    .flatMap {
      implicit rts =>
        BlazeServerBuilder[AppTask]
          .bindHttp(8080, "0.0.0.0")
          .withHttpApp(CORS(httpApp))
          .serve
          .compile
          .drain
    }
  } yield server

  val runEnv = new AppEnvironment with Console.Live with Clock.Live
  {
    val helloRepository = new HelloRepository
    {
      def getHello(name: String): ZIO[AppEnvironment, Nothing, String] = ZIO.succeed(s"Hello $name")
    }
  }

  def run(args: List[String]) =
    program
      .provide(runEnv)
      .foldM(err => putStrLn(s"Execution failed with: $err") *> ZIO.succeed(1), _ => ZIO.succeed(0))
}

/tmp/http4s/build.sbt

val Http4sVersion       = "0.20.0"
val CatsVersion         = "2.0.0"
val ZioCatsVersion      = "2.0.0.0-RC3"
val ZioVersion          = "1.0.0-RC13"
val LogbackVersion      = "1.2.3"

lazy val root = (project in file("."))
  .settings(
    organization := "example",
    name := "example",
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.12.8",
    scalacOptions ++= Seq("-Ypartial-unification"),
    libraryDependencies ++= Seq(
      "org.typelevel"              %% "cats-effect"         % CatsVersion,
      "dev.zio"                    %% "zio"                 % ZioVersion,
      "dev.zio"                    %% "zio-interop-cats"    % ZioCatsVersion,
      "org.http4s"                 %% "http4s-blaze-server" % Http4sVersion,
      "org.http4s"                 %% "http4s-dsl"          % Http4sVersion,
      "ch.qos.logback"             %  "logback-classic"     % LogbackVersion,
   ),
    addCompilerPlugin("org.spire-math" %% "kind-projector"     % "0.9.6"),
    addCompilerPlugin("com.olegpy"     %% "better-monadic-for" % "0.2.4")
  )

scalacOptions ++= Seq(
  "-deprecation",               // Emit warning and location for usages of deprecated APIs.
  "-encoding", "UTF-8",         // Specify character encoding used by source files.
  "-language:higherKinds",      // Allow higher-kinded types
  "-language:postfixOps",       // Allows operator syntax in postfix position (deprecated since Scala 2.10)
  "-feature",                   // Emit warning and location for usages of features that should be imported explicitly.
  "-Ypartial-unification",      // Enable partial unification in type constructor inference
  "-Xfatal-warnings",           // Fail the compilation if there are any warnings
)

sample execution

bash-3.2$ cd /tmp/http4s
bash-3.2$ sbt
...
sbt:example> compile
...
[info] Done compiling.
[success] Total time: 5 s, completed Oct 24, 2019 11:20:53 PM
sbt:example> run
...
[info] Running Main 
23:21:03.720 [zio-default-async-1-163838348] INFO org.http4s.blaze.channel.nio1.NIO1SocketServerGroup - Service bound to address /0:0:0:0:0:0:0:0:8080
23:21:03.725 [blaze-selector-0] DEBUG org.http4s.blaze.channel.nio1.SelectorLoop - Channel initialized.
23:21:03.732 [zio-default-async-1-163838348] INFO org.http4s.server.blaze.BlazeServerBuilder - 
  _   _   _        _ _
 | |_| |_| |_ _ __| | | ___
 | ' \  _|  _| '_ \_  _(_-<
 |_||_\__|\__| .__/ |_|/__/
             |_|
23:21:03.796 [zio-default-async-1-163838348] INFO org.http4s.server.blaze.BlazeServerBuilder - http4s v0.20.0 on blaze v0.14.0 started at http://[0:0:0:0:0:0:0:0]:8080/
23:21:11.070 [blaze-selector-1] DEBUG org.http4s.blaze.channel.nio1.SelectorLoop - Channel initialized.
23:21:11.070 [blaze-selector-1] DEBUG org.http4s.blaze.channel.nio1.NIO1HeadStage - Starting up.
23:21:11.070 [blaze-selector-1] DEBUG org.http4s.blaze.channel.nio1.NIO1HeadStage - Stage NIO1HeadStage sending inbound command: Connected
23:21:11.070 [blaze-selector-1] DEBUG org.http4s.server.blaze.Http1ServerStage$$anon$1 - Starting HTTP pipeline
23:21:11.072 [blaze-selector-1] DEBUG org.http4s.blazecore.IdleTimeoutStage - Starting idle timeout stage with timeout of 30000 ms

At this point after opening http://localhost:8080/hello/there I observed the expected output in the browser.

Hope this helps.

like image 69
jq170727 Avatar answered Mar 01 '26 19:03

jq170727



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!