Skip to content

Commit

Permalink
Write simple http4s server
Browse files Browse the repository at this point in the history
  • Loading branch information
H1rono committed Feb 23, 2024
1 parent 726faf5 commit 518c48d
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 6 deletions.
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object Dependencies {
lazy val http4s = Seq(
"org.http4s" %% "http4s-ember-server" % http4sVersion,
"org.http4s" %% "http4s-dsl" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion
)
lazy val circeGeneric = "io.circe" %% "circe-generic" % "0.14.6"
}
19 changes: 19 additions & 0 deletions src/main/scala/h1rono/BotRoutes.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package h1rono

import cats.effect.Sync
import cats.implicits._
import org.http4s.HttpRoutes
import org.http4s.dsl.Http4sDsl

object BotRoutes {
def helloWorldRoutes[F[_]: Sync](H: HelloWorld[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
HttpRoutes.of[F] { case GET -> Root / "hello" / name =>
for {
greeting <- H.hello(HelloWorld.Name(name))
resp <- Ok(greeting)
} yield resp
}
}
}
34 changes: 34 additions & 0 deletions src/main/scala/h1rono/BotServer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package h1rono

import cats.effect.Async
import com.comcast.ip4s._
import fs2.io.net.Network
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.implicits._
import org.http4s.server.middleware.Logger

object BotServer {
def run[F[_]: Async: Network]: F[Nothing] = {
val helloWorldAlg = HelloWorld.impl[F]

// Combine Service Routes into an HttpApp.
// Can also be done via a Router if you
// want to extract segments not checked
// in the underlying routes.
val httpApp = (
BotRoutes.helloWorldRoutes[F](helloWorldAlg)
).orNotFound

// With Middlewares in place
val finalHttpApp = Logger.httpApp(true, true)(httpApp)
for {
_ <-
EmberServerBuilder
.default[F]
.withHost(ipv4"0.0.0.0")
.withPort(port"8080")
.withHttpApp(finalHttpApp)
.build
} yield ()
}.useForever
}
34 changes: 34 additions & 0 deletions src/main/scala/h1rono/HelloWorld.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package h1rono

import cats.Applicative
import cats.implicits._
import io.circe.{Encoder, Json}
import org.http4s.EntityEncoder
import org.http4s.circe._

trait HelloWorld[F[_]] {
def hello(n: HelloWorld.Name): F[HelloWorld.Greeting]
}

object HelloWorld {
final case class Name(name: String) extends AnyVal

/** More generally you will want to decouple your edge representations from your internal data
* structures, however this shows how you can create encoders for your data.
*/
final case class Greeting(greeting: String) extends AnyVal
object Greeting {
implicit val greetingEncoder: Encoder[Greeting] = new Encoder[Greeting] {
final def apply(a: Greeting): Json = Json.obj(
("message", Json.fromString(a.greeting))
)
}
implicit def greetingEntityEncoder[F[_]]: EntityEncoder[F, Greeting] =
jsonEncoderOf[F, Greeting]
}

def impl[F[_]: Applicative]: HelloWorld[F] = new HelloWorld[F] {
def hello(n: HelloWorld.Name): F[HelloWorld.Greeting] =
Greeting("Hello, " + n.name).pure[F]
}
}
9 changes: 4 additions & 5 deletions src/main/scala/h1rono/Main.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package example

object Main extends App {
import org.json4s.native.JsonMethods.parse
import cats.effect.{IO, IOApp}
import h1rono.BotServer

val flakeLock = io.Source.fromFile("flake.lock").mkString
val parsed = parse(flakeLock)
println(parsed)
object Main extends IOApp.Simple {
val run = BotServer.run[IO]
}

0 comments on commit 518c48d

Please sign in to comment.