http4s

libraryDependencies += "com.avast" %% "sst-http4s-server-blaze" % "0.18.4"

There are multiple http4s-* subprojects that provide easy initialization of a server and a client. Http4s is an interface with multiple possible implementations - for now we provide only implementations based on Blaze.

Both server and client are configured via configuration case class which contains default values taken from the underlying implementations.

import cats.effect.*
import com.avast.sst.http4s.client.*
import com.avast.sst.http4s.server.*
import com.avast.sst.jvm.execution.ExecutorModule
import com.avast.sst.jvm.system.console.ConsoleModule
import org.http4s.dsl.Http4sDsl
import org.http4s.HttpRoutes
import zio.interop.catz.*
import zio.interop.catz.implicits.*
import zio.*

implicit val runtime: Runtime[ZEnv] = zio.Runtime.default // this is just needed in example

val dsl = Http4sDsl[Task] // this is just needed in example
import dsl.*

val routes = Http4sRouting.make { 
  HttpRoutes.of[Task] {
    case GET -> Root / "hello" => Ok("Hello World!")
  }
}

val resource = for {
  executorModule <- ExecutorModule.makeDefault[Task]
  console = ConsoleModule.make[Task]
  server <- Http4sBlazeServerModule.make[Task](Http4sBlazeServerConfig("127.0.0.1", 0), routes, executorModule.executionContext)
  client <- Http4sBlazeClientModule.make[Task](Http4sBlazeClientConfig(), executorModule.executionContext)
} yield (server, client, console)

val program = resource
  .use {
    case (server, client, console) =>
      client
        .expect[String](s"http://127.0.0.1:${server.address.getPort}/hello")
        .flatMap(console.printLine)
  }
runtime.unsafeRun(program)
// Hello World!

Middleware

Correlation ID Middleware

import cats.effect.*
import com.avast.sst.http4s.server.*
import com.avast.sst.http4s.server.middleware.CorrelationIdMiddleware
import com.avast.sst.jvm.execution.ExecutorModule
import org.http4s.dsl.Http4sDsl
import org.http4s.HttpRoutes
import zio.interop.catz.*
import zio.interop.catz.implicits.*
import zio.*

val dsl = Http4sDsl[Task]  // this is just needed in example
import dsl.*

implicit val runtime: Runtime[ZEnv] = zio.Runtime.default // this is just needed in example

for {
  middleware <- Resource.eval(CorrelationIdMiddleware.default[Task])
  executorModule <- ExecutorModule.makeDefault[Task]
  routes = Http4sRouting.make {
    middleware.wrap {
      HttpRoutes.of[Task] {
        case GET -> Root =>
          // val correlationId = middleware.retrieveCorrelationId(req)
          ???
      }
    }
  }
  server <- Http4sBlazeServerModule.make[Task](Http4sBlazeServerConfig.localhost8080, routes, executorModule.executionContext)
} yield server

Circuit Breaker

It is a good practice to wrap any communication with external system with circuit breaking mechanism to prevent spreading of errors and bad latency. See monix-catnap for one of the options.