scalaintermediate
HTTP Server with http4s
Build HTTP servers with http4s: routes, middleware, JSON, and streaming responses.
scalaPress ⌘/Ctrl + Shift + C to copy
import cats.effect.*
import org.http4s.*
import org.http4s.dsl.io.*
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.circe.*
import org.http4s.circe.CirceEntityCodec.*
import io.circe.generic.auto.*
import io.circe.syntax.*
import com.comcast.ip4s.*
case class User(id: Long, name: String, email: String)
case class CreateUser(name: String, email: String)
case class ErrorResponse(error: String)
object Api extends IOApp.Simple:
// In-memory store
val store = Ref.unsafe[IO, (Long, Map[Long, User])]((1L, Map.empty))
// Routes
val userRoutes: HttpRoutes[IO] = HttpRoutes.of[IO] {
// GET /users
case GET -> Root / "users" =>
for
(_, users) <- store.get
resp <- Ok(users.values.toList.asJson)
yield resp
// GET /users/:id
case GET -> Root / "users" / LongVar(id) =>
for
(_, users) <- store.get
resp <- users.get(id) match
case Some(user) => Ok(user.asJson)
case None => NotFound(ErrorResponse(s"User $id not found").asJson)
yield resp
// POST /users
case req @ POST -> Root / "users" =>
for
body <- req.as[CreateUser]
user <- store.modify { (nextId, users) =>
val user = User(nextId, body.name, body.email)
((nextId + 1, users + (nextId -> user)), user)
}
resp <- Created(user.asJson)
yield resp
// DELETE /users/:id
case DELETE -> Root / "users" / LongVar(id) =>
for
existed <- store.modify { (nextId, users) =>
((nextId, users - id), users.contains(id))
}
resp <- if existed then NoContent() else NotFound()
yield resp
}
// Health check
val healthRoutes: HttpRoutes[IO] = HttpRoutes.of[IO] {
case GET -> Root / "health" =>
Ok(Map("status" -> "ok").asJson)
}
// Middleware: request logging
def requestLogger(routes: HttpRoutes[IO]): HttpRoutes[IO] =
HttpRoutes { req =>
cats.data.OptionT.liftF(
IO.println(s"${req.method} ${req.uri}")
) >> routes.run(req)
}
// Combine routes
val app: HttpApp[IO] = (
requestLogger(userRoutes) <+> healthRoutes
).orNotFound
def run: IO[Unit] =
EmberServerBuilder
.default[IO]
.withHost(host"0.0.0.0")
.withPort(port"8080")
.withHttpApp(app)
.build
.use(_ => IO.println("Server started on :8080") >> IO.never)
.voidSponsored
Railway
Use Cases
- REST API development with http4s
- Functional HTTP servers
- Middleware and route composition
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalaintermediate
Play Framework REST Controller
Build REST APIs with Play Framework: routes, JSON serialization, async actions, and error handling.
Best for: REST API development
#scala#play
scalabeginner
Scala Hello World Application
Create a basic Scala application with main method, string interpolation, and val/var basics.
Best for: Getting started with Scala
#scala#basics
scalabeginner
Pattern Matching Fundamentals
Use Scala pattern matching with guards, type patterns, tuple patterns, and nested extractors.
Best for: Control flow with pattern matching
#scala#pattern-matching
scalabeginner
Case Classes and Objects
Define immutable data with case classes: copy, equality, destructuring, and companion objects.
Best for: Domain modeling with immutable data
#scala#case-class