Skip to content

Http4s middleware - example with a resource being used by routes #946

Description

@adamjuhasz

Hey all!

I'm trying to understand how to use natchez and natchez-http4s when the route requires a resource (such as database calls). Is there an example of this?

When trying to send the database object to the routes I get the following type error around the resource I'm trying to use in the http routes.

[error] -- [E007] Type Mismatch Error: Application.scala:54:56 
[error] 54 |      ap = ep.liftT(NatchezMiddleware.server(rootRoutes(db))).orNotFound
[error]    |                                                        ^^
[error]    |Found:    (db : modules.Database[cats.effect.IO])
[error]    |Required: modules.Database[
[error]    |  [_] =>> cats.data.Kleisli[cats.effect.IO, natchez.Span[cats.effect.IO], _]]
[error]    |
[error]    | longer explanation available when compiling with `-explain`
[error] one error found
object Application extends IOApp.Simple {
  given logger: Logger[IO] = Slf4jLogger.getLogger[IO]

  val epInIO: EntryPoint[IO] = Log.entryPoint[IO]("app")

  def server(
      xa: HikariTransactor[IO],
      httpConf: HttpConfig
  ): Resource[IO, Server] =
    for
      ep: EntryPoint[IO] <- Resource.pure(epInIO)

      db: Database[IO] = Database(xa)

      ap = ep.liftT(NatchezMiddleware.server(rootRoutes(db))).orNotFound

      server: Server <- EmberServerBuilder
        .default[IO]
        .withHost(httpConf.host)
        .withPort(httpConf.port)
        .withHttpApp(ap)
        .build
    yield server

  override def run: IO[Unit] =
    for
      dbConf: DatabaseConfig <- DatabaseConfig.getConfig[IO]
      httpConf <- HttpConfig.getConfig[IO]

      program: Resource[IO, Unit] = for
        xa: HikariTransactor[IO] <- Database.makePostgresResource[IO](dbConf)
        _x: Server  <- server(xa, httpConf)
      yield ()

      _ <- program.use(_ => IO.never)
    yield ExitCode.Success
}
def rootRoutes[F[_]: Trace: Concurrent: Logger](database: Database[F])(implicit
    ev: MonadError[F, Throwable]
): HttpRoutes[F] = Router(
  "/"            -> (HealthRoutes[F].routes),
  "/v1/security" -> (SecuritiesRoutes[F](database).routes)
)
object Database:
  def apply[F[_]: Async: Logger](
      xa: Transactor[F]
  ): Database[F] = new Database[F](
    securities = SecuritiesRepository[F](xa),
    identifiers = IdentifierRepository[F](xa),
    securityTree = SecurityTreeRepository[F](xa),
    events = EventRepository[F](xa)
  )

  def makePostgresResource[F[_]: Async: Logger](
      config: DatabaseConfig
  ): Resource[F, HikariTransactor[F]] = for
    ec <- ExecutionContexts.fixedThreadPool(config.nThreads)
    xa <- HikariTransactor.newHikariTransactor[F](
      driverClassName = "org.postgresql.Driver",
      url = s"jdbc:${config.url}",
      user = config.user,
      pass = config.pass,
      connectEC = ec,
      logHandler = Some(mkLogger[F])
    )
  yield xa

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions