Last active
February 3, 2026 20:25
-
-
Save dacr/62d5760356927be012449fbfad39a1f0 to your computer and use it in GitHub Desktop.
tapir with iron for data validity and better documentation / published by https://github.com/dacr/code-examples-manager #c100bdc5-c628-4aa7-86f3-f838a07a6266/5bbc173d6e90db5befa78aa6ee249eb31270f55f
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // summary : tapir with iron for data validity and better documentation | |
| // keywords : scala, zio, tapir, iron, @testable, @exclusive | |
| // publish : gist | |
| // authors : David Crosson | |
| // license : Apache License Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) | |
| // id : c100bdc5-c628-4aa7-86f3-f838a07a6266 | |
| // created-on : 2024-06-06T13:37:39+02:00 | |
| // managed-by : https://github.com/dacr/code-examples-manager | |
| // run-with : scala-cli $file | |
| // test-with : curl -L http://127.0.0.1:8080/hello/david | |
| // --------------------- | |
| //> using scala "3.4.2" | |
| //> using dep "com.softwaremill.sttp.tapir::tapir-zio:1.10.9" | |
| //> using dep "com.softwaremill.sttp.tapir::tapir-zio-http-server:1.10.9" | |
| //> using dep "com.softwaremill.sttp.tapir::tapir-iron:1.10.9" | |
| //> using dep "com.softwaremill.sttp.tapir::tapir-json-zio:1.10.9" | |
| //> using dep "com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.9" | |
| //> using dep "io.github.iltotore::iron-zio:2.5.0" | |
| //> using dep "io.github.iltotore::iron-zio-json:2.5.0" | |
| // --------------------- | |
| import zio.* | |
| import zio.json.* | |
| import zio.http.Server | |
| import io.github.iltotore.iron.* | |
| import io.github.iltotore.iron.constraint.all.* | |
| import io.github.iltotore.iron.zioJson.given | |
| import sttp.tapir.ztapir.* | |
| import sttp.tapir.server.ziohttp.ZioHttpInterpreter | |
| import sttp.tapir.swagger.bundle.SwaggerInterpreter | |
| import sttp.tapir.json.zio.* | |
| import sttp.tapir.Schema | |
| import sttp.tapir.generic.auto.* | |
| import sttp.tapir.codec.iron.* | |
| import sttp.apispec.openapi.Info | |
| object WebApp extends ZIOAppDefault { | |
| enum Gender derives JsonCodec { | |
| case Female, Male | |
| } | |
| object Gender { | |
| given JsonEncoder[Gender] = JsonEncoder[String].contramap(p => p.toString) | |
| given JsonDecoder[Gender] = JsonDecoder[String].map(p => Gender.valueOf(p)) | |
| given Schema[Gender] = Schema.derivedEnumeration.defaultStringBased | |
| } | |
| case class Salute( | |
| name: String :| Not[Empty] :| Alphanumeric, | |
| age: Option[Int :| Positive] /*DescribedAs "Age should be positive"*/, | |
| gender: Option[Gender], | |
| nicknames: List[String :| (MinLength[3] & MaxLength[6])] :| Not[Empty] | |
| ) | |
| object Salute { | |
| given JsonEncoder[Salute] = DeriveJsonEncoder.gen | |
| given JsonDecoder[Salute] = DeriveJsonDecoder.gen | |
| } | |
| case class Greeting(message: String) derives JsonCodec | |
| // -------------------------------------------------- | |
| def helloLogic(salute: Salute): UIO[Greeting] = { | |
| val nicknames:List[String] = salute.nicknames | |
| for { | |
| nickName <- Random.shuffle(nicknames).map(_.headOption) // .when(!salute.nicknames.isEmpty) | |
| name = nickName.getOrElse(salute.name) | |
| message = salute match { | |
| case Salute(name, Some(foundAge:Int), _, _) if foundAge < 18 => s"Hi $name" | |
| case Salute(name, Some(foundAge), Some(Gender.Female), _) => s"Hello madam $name" | |
| case Salute(name, Some(foundAge), Some(Gender.Male), _) => s"Hello mister $name" | |
| case Salute(name, _, _, _) => s"Good morning $name" | |
| } | |
| } yield Greeting(message) | |
| } | |
| val helloEndPoint = | |
| endpoint | |
| .tag("Greetings") | |
| .name("hello") | |
| .description("Returns stateful greeting") | |
| .post | |
| .in("hello") | |
| //.in(jsonBody[Salute].example(Salute("Joe", None, None, List("joe")))) | |
| .in(jsonBody[Salute]) | |
| .out(jsonBody[Greeting]) | |
| val helloRoute = helloEndPoint.zServerLogic[Any](helloLogic) | |
| // -------------------------------------------------- | |
| val apiDocRoute = | |
| SwaggerInterpreter() | |
| .fromServerEndpoints( | |
| List(helloRoute), | |
| Info( | |
| title = "Greeting API", | |
| version = "1.0", | |
| description = Some("Everything required to be polite") | |
| ) | |
| ) | |
| // -------------------------------------------------- | |
| val routes = ZioHttpInterpreter().toHttp(List(helloRoute) ++ apiDocRoute) | |
| // -------------------------------------------------- | |
| val server = for { | |
| _ <- ZIO.log("API documentation : http://127.0.0.1:8080/docs") | |
| _ <- Server.serve(routes) | |
| } yield () | |
| override def run = server.provide(Server.default) | |
| } | |
| WebApp.main(Array.empty) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment