http4s / http4s

A minimal, idiomatic Scala interface for HTTP
https://http4s.org/
Apache License 2.0
2.56k stars 789 forks source link

How to use different services/httpApp in the same EmberServerBuilder? #6321

Open olveraa opened 2 years ago

olveraa commented 2 years ago

Hello guys, We are trying to migrate an old http4s version to the current latest stable version of 0.23.11 http4s. In the past, we were using BlazeBuilder[IO] .mountService(notFoundHandler(accountsService.corsService), "/") .mountService(notFoundHandler(notificationService.corsService), "/notifications") where val corsService = CORS(service) and val service: HttpService[IO] = new RhoService[IO]

We don't know how to mount the same structure in a EmberServerBuilder. The only way to attach services is using withHttpApp , but we don't know how to add multiples services as in the past without replacing the service. There is not a mountService function.

Any help will be appreciated. Also I can give more information. Thanks!!!

ChristopherDavenport commented 2 years ago

You should be able to build the equivalent behavior using routers. define is almost a direct correlation. If that doesn't work we can explore how we can do better.

https://github.com/http4s/http4s/blob/series/0.23/server/shared/src/main/scala/org/http4s/server/Router.scala

olveraa commented 2 years ago

Thanks @ChristopherDavenport for the response. The thing is that we are using Rho library also, and we don't have an idea how to relate everything. Let me share a bit of our code and let's see what you think.

The old code that we have is something like, using http4s v=0.18.23 and rho v=0.18.0.

BlazeBuilder[IO]
    .bindHttp(8080, "0.0.0.0")
    .withIdleTimeout(10.minute)
    .mountService(notFoundHandler(accountsService.corsService), "/")
    .mountService(notFoundHandler(notificationService.corsService), "/notifications")

and inside one of the services (accountsService) that we have follows the form as:

class AccountsApi(....) {

      val service: HttpService[IO] = new RhoService[IO] {
             private val accountIdParam = pathVar[String]("id", "Id of Account")
             ........

            "Get Account by Id" **
              GET / "accounts" / accountIdParam |>> { id: String =>
               ......
      }
     ....
      val methodConfig = CORSConfig(anyOrigin = true, anyMethod = true, allowCredentials = true, maxAge = 1.day.toSeconds)

  val corsService = CORS(service)

} 

The goal of this is updating everything to the latest version of Rho v0.23.0-RC1 (even if it is a release candidate) and to make it work synchronizing with http4s. So we are coding something like this, using http4s v=0.23.11 and rho v=0.23.0-RC1.

In the server side:

EmberClientBuilder.default[IO].build.use {
                EmberServerBuilder
                  .default[IO]
                  .withHost(ipv4"0.0.0.0")
                  .withPort(port"8080")
                  .withIdleTimeout(10.minute)
                  .withHttpApp(accountsService.orNotFound)
                  .withHttpApp(notificationService.orNotFound)
                  .build
                  .use(_ => IO.never)
                  .as(ExitCode.Success)
}

In the account service example:

import org.http4s.rho.swagger.syntax.io._

class AccountsApi(...) extends RhoRoutes[IO] {
   .... 
   private val accountIdParam = pathVar[String]("id", "Id of Account")
   ....

  "Get Account by Id" **
    GET / "accounts" / accountIdParam |>> { id: String =>
        ......

}

But we think that using withHttpApp it will replace one service with the other.

So, will you use the Router here in this scenario? Is it possible to use this, or this better alternatives for that? Any help please, we are desperated! Thanks!!!