Skip to content

Instantly share code, notes, and snippets.

@kciesielski
Last active October 18, 2023 07:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kciesielski/8431b0cea35e5778cb208dc2c3610dcf to your computer and use it in GitHub Desktop.
Save kciesielski/8431b0cea35e5778cb208dc2c3610dcf to your computer and use it in GitHub Desktop.
Tapir + Circe configured codec derivation
//> using scala 3.3.1
//> using dep com.softwaremill.sttp.tapir::tapir-netty-server:1.8.0
//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.8.0
//> using dep com.softwaremill.sttp.tapir::tapir-json-circe:1.8.0
package com.softwaremill
import sttp.tapir.*
import DragonApi.*
import ResponseApi.*
import sttp.tapir.generic.auto.*
import io.circe.*
import io.circe.derivation.*
import sttp.tapir.generic.{Configuration => SchemaConfiguration}
import sttp.tapir.json.circe.*
import sttp.tapir.server.ServerEndpoint
import sttp.tapir.swagger.bundle.SwaggerInterpreter
import sttp.tapir.Schema
import sttp.tapir.Schema.SName
import sttp.tapir.server.netty.NettyFutureServer
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}
import ExecutionContext.Implicits.global
import scala.io.StdIn
object Endpoints:
val createAnimal: ServerEndpoint[Any, Future] = endpoint.post
.in("dragons")
.in(jsonBody[Dragon])
.out(jsonBody[CreateResponse])
.serverLogic((dragon: Dragon) =>
dragon match
case IceDragon(name) =>
//Ice business logic here
Future.successful(Right(CreateResponse(s"Hello, Ice Dragon $name")))
case FireDragon(name) =>
//Fire business logic here
Future.successful(Right(CreateResponse(s"Hello, Fire Dragon $name")))
)
val apiEndpoints: List[ServerEndpoint[Any, Future]] = List(createAnimal)
val docEndpoints: List[ServerEndpoint[Any, Future]] = SwaggerInterpreter()
.fromServerEndpoints[Future](apiEndpoints, "growing-harrier", "1.0.0")
val all: List[ServerEndpoint[Any, Future]] = apiEndpoints ++ docEndpoints
object ResponseApi:
given Configuration = Configuration.default
case class CreateResponse(msg: String) derives ConfiguredCodec
object DragonApi:
given Configuration = Configuration.default.withDiscriminator("dragonType")
given SchemaConfiguration = SchemaConfiguration.default.withDiscriminator("dragonType")
given Schema[Dragon] = Schema.derived
sealed trait Dragon derives ConfiguredCodec:
val name: String
case class IceDragon(name: String) extends Dragon
case class FireDragon(name: String) extends Dragon
@main def run(): Unit =
val port = sys.env.get("HTTP_PORT").flatMap(_.toIntOption).getOrElse(8080)
val program: Future[Unit] =
for
binding <- NettyFutureServer().port(port).addEndpoints(Endpoints.all).start()
_ <- Future {
println(s"Go to http://localhost:${binding.port}/docs to open SwaggerUI. Press ENTER key to exit.")
StdIn.readLine()
}
stop <- binding.stop()
yield ()
Await.result(program, Duration.Inf)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment