Pekko HTTP (Akka HTTP) style DSL directives for http4s with cats-effect IO. About 85% of all directives have been ported. Includes a test kit similar to Pekko's.
Cross-compiled for JVM, Scala.js, and Scala Native. Supports Scala 2.13 and Scala 3.
// build.sbt
libraryDependencies += "pl.iterators" %% "http4s-stir" % "0.7.1"
libraryDependencies += "pl.iterators" %% "http4s-stir-testkit" % "0.7.1" % TestA complete example you can run with scala-cli run .:
// Main.scala
//> using dep pl.iterators::http4s-stir::0.7.1
//> using dep org.http4s::http4s-ember-server::0.23.33
//> using dep org.http4s::http4s-circe::0.23.33
//> using dep io.circe::circe-generic::0.14.15
//> using dep org.typelevel::cats-effect::3.7.0
import org.http4s.Status
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.circe.CirceEntityEncoder.*
import org.http4s.circe.CirceEntityDecoder.*
import io.circe.*
import io.circe.generic.semiauto.*
import cats.effect.{IO, IOApp}
import pl.iterators.stir.server.*
import pl.iterators.stir.server.Directives.*
var orders: List[Item] = Nil
final case class Item(name: String, id: Long)
final case class Order(items: List[Item])
given Codec[Item] = deriveCodec[Item]
given Codec[Order] = deriveCodec[Order]
def fetchItem(itemId: Long): IO[Option[Item]] = IO.delay {
orders.find(o => o.id == itemId)
}
def saveOrder(order: Order): IO[List[Item]] = {
orders = order.items ::: orders
IO.delay(orders)
}
val route: Route =
concat(
get {
pathPrefix("item" / LongNumber) { id =>
val maybeItem: IO[Option[Item]] = fetchItem(id)
onSuccess(maybeItem) {
case Some(item) => complete(item)
case None => complete(Status.NotFound)
}
}
},
post {
path("create-order") {
entity(as[Order]) { order =>
val saved: IO[List[Item]] = saveOrder(order)
onSuccess(saved) { _ =>
complete("order created")
}
}
}
}
)
object Main extends IOApp.Simple {
val run = EmberServerBuilder
.default[IO]
.withHttpApp(route.toHttpApp) // seals the route: 404, 405, 400, 401 as Pekko answers them
.build
.use(_ => IO.never)
}http4s-stir includes a test kit with familiar ~> routing test syntax:
// Main.test.scala
//> using test.dep pl.iterators::http4s-stir-testkit:0.7.1
//> using test.dep org.http4s::http4s-circe:0.23.33
//> using test.dep org.specs2::specs2-core:5.5.8
import org.http4s.Status
import org.http4s.circe.CirceEntityEncoder.*
import org.http4s.circe.CirceEntityDecoder.*
import cats.effect.IO
import cats.effect.unsafe.IORuntime
import org.specs2.mutable.Specification
import pl.iterators.stir.testkit.Specs2RouteTest
class MainRoutesSpec extends Specification with Specs2RouteTest {
override implicit val runtime: IORuntime = IORuntime.global
sequential
"The routes" should {
"create order" in {
Post("/create-order", Order(List(Item("foo", 42)))) ~> route ~> check {
responseAs[String] must contain("order created")
}
}
"retrieve an item if present" in {
orders = List(Item("foo", 42))
Get("/item/42") ~> route ~> check {
responseAs[Item] must beEqualTo(Item("foo", 42))
}
}
"return 404 if item is not present" in {
orders = List.empty
Get("/item/42") ~> route ~> check {
status must beEqualTo(Status.NotFound)
}
}
}
}Run with scala-cli test ..
For a more comprehensive example showcasing additional directives, see examples/Service.scala. Run it locally with sbt ~examples/reStart.
Full documentation is available at theiterators.github.io/http4s-stir.
- Installation
- Quick Start
- Route Composition
- Directives
- Rejection Handling
- Exception Handling
- Testing
- http4s-dsl Compatibility
- Migration from Pekko/Akka HTTP
There's a compatibility layer, Http4sDirectives, that lets you embed existing http4s-dsl routes within stir routes.
Every Pekko HTTP directive category has a counterpart. What differs:
- Directives tied to the Pekko runtime (
extractExecutionContext,extractMaterializer,extractActorSystem,withLog,withSettingsand friends,onCompleteWithBreaker) have no equivalent; settings-driven behaviour is configured per directive instead. DebuggingDirectiveshave a different API (flags andLogRedactioninstead ofLoggingMagnet;logRequestResultWithtakes Pekko's per-result function).Marshaller.oneOfandAccept-Charsetnegotiation are not ported; resource directives work on the JVM only.- Testkit differences: synchronous execution, and the WebSocket probe talks to the handler without the server's ping/pong and defragmentation.
See the migration guide for the full list, including the behaviour changes in 0.7.
stir something up (pv)
to cause an unpleasant emotion or problem to begin or grow
Some love http4s DSL but dislike Pekko's. Others feel the opposite. This library stirs things up by combining both.
Issues and PRs welcome at github.com/theiterators/http4s-stir.
Apache License 2.0. See LICENSE.
http4s-stir incorporates code adapted from Pekko HTTP, a fork of Akka HTTP.
