Where communities thrive

  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
Repo info
  • Jun 12 15:26

    mergify[bot] on master

    Update magnolia-core to 2.0.0-M8 Merge pull request #1316 from s… (compare)

  • Jun 12 15:26
    mergify[bot] closed #1316
  • Jun 12 14:47
    scala-steward closed #1314
  • Jun 12 14:47
    scala-steward commented #1314
  • Jun 12 14:47
    scala-steward opened #1316
  • Jun 12 10:11
    adamw commented #852
  • Jun 12 10:10
    adamw opened #1315
  • Jun 12 10:10

    adamw on more-scala3

    Enable more projects for scala3 (compare)

  • Jun 12 07:07
    scala-steward opened #1314
  • Jun 11 22:54
    scala-steward opened #1313
  • Jun 11 13:48

    mergify[bot] on scala3


  • Jun 11 13:47
    adamw closed #1204
  • Jun 11 13:12
    adamw commented #1312
  • Jun 11 13:12

    adamw on master

    Update OpenapiCodegenTask.scala… Merge pull request #1312 from k… (compare)

  • Jun 11 13:12
    adamw closed #1312
  • Jun 11 13:05

    mergify[bot] on master

    Update sbt-scalajs, scalajs-lib… Merge pull request #1299 from s… (compare)

  • Jun 11 13:05
    mergify[bot] closed #1299
  • Jun 11 13:05
    adamw synchronize #1204
  • Jun 11 13:05

    adamw on scala3

    Fix docs (compare)

  • Jun 11 13:01
    kastoestoramadus opened #1312
Gilad Hoch

I'm using tapir, and really liking it so far. But there's something I find a bit odd in the default behaviour of endpoint input, but maybe I'm not using it correctly?
Anyway, the usecase is simple. consider some simple endpoint like:

val getUsers: Endpoint[Int, String, String, Any] = {
  val userId = path[Int].description("The user ID is a 32-bit signed integer")
    .name("get user")
    .in("api" / "users" / userId)

I would expect that a call like: GET <host>/api/users/foo would result with something like:
400 BadRequest : 'foo' is not a valid Int
instead we get:
500 Internal Server Error : There was an internal server error.

Looking at the code, I see that path has a context bound for a codec, which I guess is always this:

implicit val int: Codec[String, Int, TextPlain] = stringCodec[Int](_.toInt).schema(Schema.schemaForInt)

and in the implementation, it's just string codec mapped with _.toInt, leaving the validator as Validator.pass.

So this seems a bit weird to me, since clearly what we want in such a case is to validate the primitives properly, thus returning 400, and not 500 in case of invalid input.

  1. Is there anything I missed? Is there an easy fix?
  2. If not, would you like me to open an issue for that?


13 replies
Doug Clinton
I have a case class for which I have a custom circe encoder that adds an extra field to the generated json. How can I modify the schema to add a description of the extra field?
5 replies
Thijs Broersen
I upgraded Tapir from 0.17.9 to 0.17.19 and now RedocHttp4s is broken. I get a 308 when I want to view the rendered docs. Could not find any related (bug-)ticket in github. I see some changes in the source-code but cannot find any migration instructions. Anyone got the same issue?
Thijs Broersen
turns out contextPath cannot be Nil or an empty String
6 replies
also RedocHttp4s and SwaggerHttp4s have different implementations (e.g. contextPath is a List[String] for the first and String for the latter)
would adding a server interpreter with zio-http be considered?
i know we have zio-http4s already and zio-http is not official zio but 3rd party, although is endorsed by zio devs
is there a guide to write your own server interpeter. what is the interface?
3 replies
Mark Hammons
how difficult would it be to add custom authentication and one-of authentication to tapir?
is it on the roadmap at all at the moment? I am wanting to adopt tapir for a project, but our auth needs (one-of 3 possible auths must be met, one of the auths is an non-conforming "oauth") block us actually transitioning to tapir
15 replies
Mathieu Prevel
Hi guys,
I am using tapir in a multi modules project (frontend, backend, shared).
If my understanding is good I should define the endpoints in the shared module, the server interpreter in the backend module and the client interpreter in the frontend (or better, the shared module).
My models also use newtype and so my project uses tapir-newtype integration.
Nonetheless there is no release of tapir-newtype for scala.js
Is there a limitation about publishing this artifact for scala.js ?
5 replies
Hi @mprevel I would like to use tapir on a scala.js frontend as well, is there any example you could point to? After reading https://tapir.softwaremill.com/en/latest/client/sttp.html I have been able to send a request using the FetchBackend. But how to implement endpoints? I have added to my dependencies
"com.softwaremill.sttp.tapir" %%% "tapir-sttp-client" % "0.18.0-M2" but I am struggling to find a way.
I might be mistaken, but looking at the errors it seems that one of the issues is that part oftapir-sttp-client is relying on methods that don't have a counterpart in scala.js, such as java.time.Instant.atZone(java.time.ZoneId)java.time.ZonedDateTime or java.time.LocalTime.parse(java.lang.CharSequence)java.time.LocalTime. Has anyone experience on Tapir in scala.js?
7 replies
Hi there! Did anybody had a chance to look at my PR with mock-server integration? softwaremill/tapir#1097 I would appreciate someone can join the discussion about testing so that I can go forward
1 reply
Vadim Agishev

Hi there!

Could you answer me, why the type ZServerEndpoint[R, I, E, O] = ServerEndpoint[I, E, O, Any, RIO[R, *]] is coupled to RIO, where error type is Throwable instead of using more flexible ZIO[R, E, *]?

I have a problem, I have an endpoint of ServerEndpoint[(String, Unit), ApiError, String, Any, ZIO[Any, ApiError, *]] type where type parameter E is the my custom type ApiError <: Throwable and I can't use ZHttp4sServerInterpreter because of type mismatch error: ZIO[Any, ApiError, ] can't be used in RIO[Any, ] context.
Thanks in advance!

6 replies
Martin Allaire
Howdy guys, has .toDirective been removed or migrated as an option from AkkaHttpServerInterpreter in v0.18?
2 replies

Hi Adam, I'm excited about a new version of Tapir. I upgraded Tapir to 0.18 M4. Minor changes mostly, though I wanted to bring up a few items before opening tickets.

  1. The respond method in sttp.tapir.server.interceptor.decodefailure.DefaultDecodeFailureHandler, has these two parameters, badRequestOnPathErrorIfPathShapeMatches & badRequestOnPathInvalidIfPathShapeMatches. I don't think their semantics have changed in the new version. By setting both to true, I'm expecting any mismatch in the shape of the URL should result a 400-BadRequest. However, currently those tests that were designed to test this behavior in my test suites are failing because the returning status code is now 404-NotFound. I can see in the code that it seems to return 400-BadRequest (Some(onlyStatus(StatusCode.BadRequest))) but I'd like to confirm with you if this is a bug.

  2. In the new sttp.tapir.server.http4s.Http4sServerOptions, it provides Log.defaultServerLog as the default implementation. My problem with the current impl is that it doesn't allow me to override the logAllDecodeFailures parameter in the DefaultServerLog constructor. I have this flag as an app setting so that I can turn it on for troubleshooting purpose. The current impl of Log.defaultServerLog returns ServerLog[F[Unit]], so I can't call copy to set the logAllDecodeFailures parameter myself. I can submit a PR for this but if you think it's trivial enough that you can squeeze in a fix in the next release, I'd leave it to you then.

  3. Also, for the implementation of customInterceptors in sttp.tapir.server.http4s.Http4sServerOptions, right now the implementation uses ExecutionContext.Implicits.global. I wonder if it makes sense to make it a parameter so it can be passed in from call sites. With the current implementation, in order to provide a different executor, the only choice for me as a user is to re-implement customInterceptors myself.

Please let me know your thoughts on these. Thank you again for all your hard work for this amazing lib. Looking forward to the official release.

18 replies

Hi, I need some help. I am upgrading Tapir from v 0.16.16 to 0.17.19. My http routes are based off this github project https://github.com/saraiva132/zio-cats-backend from the zio website resources section. In short the old routes are using the toRoutesR variant where i was passing the environment in via URIO eg

object ShortenRoutes {
    val routes: URIO[BitlyEnv, HttpRoutes[Task]] =
        ShortenApi.getShortenEndpoint.toRoutesR {
          case (reqCtx, url) =>


I am trying to convert it to something like

object ShortenRoutes {
    val routes: HttpRoutes[RIO[BitlyEnv, *]] = ZHttp4sServerInterpreter.from(ShortenApi.getShortenEndpoint) { req =>
        val (reqCtx, url) = req

I am using http4s server where previously i had

  private val appRoutes: URIO[AppEnv, HttpApp[Task]] =
    for {
      healthRoutes  <- HealthRoutes.routes
      shortenRoutes <- ShortenRoutes.routes
      docsRoutes     = new SwaggerHttp4s(yaml).routes[Task]
    } yield {
      (healthRoutes <+> shortenRoutes <+> docsRoutes).orNotFound

and in my BlazeServerBuilder I had

    for {
      app                            <- appRoutes
      config                         <- getConfig[HttpServerConfig]
      implicit0(rts: Runtime[Clock]) <- ZIO.runtime[Clock]
      ec                             <- ZIO.descriptor.map(_.executor.asEC)
      _ <-
          .bindHttp(config.port, config.host)
    } yield ()

The new code looks like

     BlazeServerBuilder[RIO[BitlyEnv, *]](ec)
    .withHttpApp(Router("/" -> (ShortenRoutes.routes <+> HealthRoutes.routes <+> new SwaggerHttp4s(yaml).routes)).orNotFound)

where BitlyEnv contains Clock

The ShortenRoutes takes BitlyEnv as the environment, where as the HealthRoutes only takes Clock since its method signature is HttpRoutes[RIO[Clock, *]].

The compiler complains about type mismatches unless i pass in the BitlyEnv to the HealthRoute as well.

New to zio/http4s and pure functional programming so I am struggling to figure out what I am doing wrong or how to have multiple routes take in different environments and make the compiler happy. Any help would be appreciated. Again , i cant really share all the code, but as mentioned, its based on the github example above specifically the http.routes package

12 replies
Edvin Lundberg
Hi, I have a server endpoint defined with tapir. The logic simply calls another service with sttp client. How could I forward the HttpError from the sttp client as the tapir endpoint error?
4 replies

Hello, I was playing with tapir a bit and I struggle a bit with the type involved in route definition.
I'm currently using this code to create a ZLayer from my services

  // type Routes[T] = ZIO[Clock, Throwable, T]
  // type ZRoutes   = HttpRoutes[Routes]
  def swaggerRoutes: ZRoutes =
    new SwaggerHttp4s(swaggerDefinition, contextPath = "docs").routes[Routes]

  def routes: URLayer[Has[GameService], Has[ZRoutes]] =
    ZLayer.fromService[GameService, ZRoutes] { gameService =>
      val r = ZHttp4sServerInterpreter
            endpoints.startGame.zServerLogic(_ => gameService.startGame()),
      r <+> swaggerRoutes

And I wanted to change my code to use method from gameService's companion object, instead of using the ZLayer.fromServices so I ended up doing that

type RoutesTest[T] = ZIO[Has[GameService] with Has[Clock.Service], Throwable, T]
  type FinalRoutes   = HttpRoutes[RoutesTest]
  def routes2: FinalRoutes = {
    val r: HttpRoutes[RoutesTest] = ZHttp4sServerInterpreter
          endpoints.startGame.zServerLogic(_ => GameService.startGame())
    r <+>
      new SwaggerHttp4s(swaggerDefinition, contextPath = "docs").routes[RoutesTest]

But I don't really like working with those HttpRoutes and Kleisli. Is there a way for me to create a ZIO layer from this ? Or is it not the way it's supposed to work, and having a ZLayer around those HttpRoutes is not recommended

4 replies
Andy Czerwonka

about to take on an upgrade from 0.17.0-M9, so wondering if someone can point me to key changes and/or a migration strategy? Just looking at the docs and comparing it to my imports, looks like some significant breaking changes. E.g, my imports

object MyTapir
    extends Tapir
    with TapirAkkaHttpServer
    with TapirOpenAPICirceYaml
    with TapirAliases
    with ValidatorDerivation
    with SchemaDerivation {

I see the TapirAkkaHttpServer import has changed, I'm curious about ValidatorDerivation and others. I also see that someone contributed Json4s support, so I'll need to bring that in.

2 replies
Andy Czerwonka

Looks like a few significant breaking changes. In reverse order:

  1. https://github.com/softwaremill/tapir/releases/tag/v0.18.0-M1
  2. https://github.com/softwaremill/tapir/releases/tag/v0.17.0
  3. https://github.com/softwaremill/tapir/releases/tag/v0.17.0-M9

If there is anything else I should look out for, please let me know.

1 reply

Is there a way to combine EndpointInput using or instand of and. My goal would be to support either
path/to/session/XXXXX or a custom header x-custom-session = XXXXX
I've defined those two EndpointInput:

  val sessionHeader: EndpointInput[UUID]    = header[UUID]("session")
  val sessionHeaderUrl: EndpointInput[UUID] = path[UUID]("x-custom-session")

But I don't know how to combine them

2 replies

Hi there! I have a question regarding testing. Often, I find myself in the situation where I don't just have an Endpoint but a ServerEndpoint with a bit of logic that I'd like to test. Example:

def twice(logic: IO[String]): ServerEndpoint[Unit, String, String, Any, IO] =
      .errorOut(stringBody) // (*)
      .serverLogic { _ =>
          .map(s => s"$s$s")
          .map {
            case Left(throwable) => Left(throwable.getMessage)
            case Right(s)        => Right(s)

The SttpBackendStub (from the docs) is super helpful to test just the first part (i.e., up to (*)) and of course, I could test the logic inside .serverLogic separately, by factoring it out into a function.

However, if I want a more end-to-end type test, is there an easy way to do this?

Something like (I'm phantasizing here):

val backend = SttpBackendStub.forServerEndpoint(twice(IO("bar")))
        .body should equal(Right("""barbar"""))


3 replies
Adam Warski

Survey time! Which authentication features are you using and how? What are you missing! Let us know to help shape tapir moving forward :)


Thank you!

2 replies
Loránd Szakács
I looked on maven for the: tapir-http4s-client, and no such artifact seems to exist. Not even something similar. Am I missing something?
1 reply
This is not really about Tapir but more on a specific implementation.
I have a Play backend where authentication is done using the Silhouette Play library. So there is a SecuredAction instead of a Play action.
I can perfectly integrate Tapir like done for example in https://github.com/gaeljw/tapir-play-sample.
But I am struggling to place the Silhouette validation bit into the authentication function called by the partial server endpoint.
Has anyone already used Tapir with Play + Silhouette and can give me some hints?
1 reply
Denis Novac
Hi everyone. I found this issue on the GitHub about Dotty support: softwaremill/tapir#852 . I am wondering why does Tapir need to wait Magnolia update now when Scala 3 can do recursive derivation out of the box?
@DenisNovac: I guess it's not necessary to wait, though I did see magnolia has merged a PR for Scala 3 support now. So a question is if one wants to rely on magnolia, or just port all the magnolia-stuff to the native tooling for derivation. I do hope tapir will be ready for Scala 3 soon, it's the one library I am waiting for to make the jump to complete Scala 3 build in a project I am working on.
Denis Novac
@heksenlied:matrix.org they have closed something, but there is no merge and Scala 3 support yet as far as i see: ghostdogpr/caliban#847
I am not sure why the PR was listed as closed, as all the commits are in master.
They haven't made a release yet, though I do hope they'll make one soon. That should speed up the porting of everything that relies on magnolia.
Magnolia for Scala 3 might be released in a few days hopefully – once that is done, it shouldn't be that much work moving tapir onto Scala 3, I imagine.
Anyone familiar enough with the project that they can help me figure out what I need to change in the build.sbt to try compile the zio integrations for 3.0.0-RC1 and publish them to an artifactory instance?
1 reply
Adam Warski
@heksenlied:matrix.org there's a couple of macros (I think all of them Schema-related) that need to be ported. The modify macros resemble closely quicklens, so as a prerequisite, I'm working (as we speak - though most of the work is done by @KacperFKorban) on porting quicklens to Scala 3 macros (https://github.com/softwaremill/quicklens/pull/66). That should also be a good learning experience so I'm hoping after implementing that, I'll be able to port tapir relatively quickly. But who knows what suprises lay ahead :)
I was wondering about using Magnolia vs Mirrors directly as well. Even though the layer is now much thinner, I think Magnolia still provides an more developer-friendly interface. So that's what I plan to use at least initially
Hi, in an streaming endpoint how to return an error response in 4xx or 5xx range when the returned stream is in error or empty ?
4 replies
Octav Zaharia

hi, where I can find a simple example of a route that downloads a binary file?equivalent of this older code :

      .out(header(HeaderNames.ContentType, "application/pdf"))
      .out(streamBody[Source[ByteString, Any]](schemaFor[Array[Byte]], CodecFormat.OctetStream()))

I need it to return the same type which is Endpoint[Request, ApiError, Source[ByteString, Any], Source[ByteString, Any]]

1 reply
Ahmet Turk
Hi all, I just had a hard time to setup simple tapir project due to the missing kind-projector. I see that -Ypartial-unification mentioned in both docs and readme but there was no indication that I needed kind-projector. Do you think kind-projector should be mentioned where it's applicable?
4 replies
Hi, I've found two issues related to ZIO and prepared fixes. Could you take a look softwaremill/tapir#1195
6 replies
Nick Childers
Im looking to upgrade from sttp2 to sttp3 and there is an endpoint abstracted over its streaming type that im not sure how to write with the new capabilities model - eg, how would I take something like this: https://github.com/softwaremill/tapir/blob/master/examples/src/main/scala/sttp/tapir/examples/StreamingAkkaServer.scala but define the endpoint in such a way that its still a streaming endpoint, but not pinned to AkkaStreams ?
16 replies
Ender Tunc
Hi, I just created a PR to add zio-json support. Please let me know if anything needs to be improved or fixed.
Hi :) Just updated my PR with tapir/mock-server integration. Can somebody have a look? softwaremill/tapir#1097
1 reply
Oleg Chernykh

Hi! I have a json as input of endpoint, which contain a fields like Int and String. I want to provide a specific validation for each field. I've read the documentation for tapir and found out that:
To introduce unique types for primitive values, you can use value classes or type tagging.

So, I wrapped each primitive field to value class for example
case class Login(v: String) extends AnyVal and when I send a request a have an error such as
Invalid value for: body (Attempt to decode value on failed cursor at 'login.v')

Could somebody help me?

*case class Login(v: String) extends AnyVal
case class Password(v: String) extends AnyVal
case class Email(v: String) extends AnyVal
case class PassportNumber(v: Int) extends AnyVal

case class UserRegistration(login: Login,
password: Password,
email: Email,
passportNumber: PassportNumber)

val signInEndpoint
: Endpoint[UserRegistration, EndpointError, StatusCode, Any]*

2 replies
Adam, I saw this post on reddit. This is a new java lib that manages feature flags in real-time. I think basically it tags endpoints and turns them on and off at runtime, similar mechanism like AspectJ's.... I wonder if this is a suitable idea for Tapir :) I'm sure a lot of people will find it useful.
4 replies
What would make Uri.parse(...) actually return a Left? It seems to accept any string as a valid Uri.
1 reply
Jens Grassel

Hi, just stumbled upon this.Out required in streaming context and don't know what to make of it.

found   : Either[sttp.model.StatusCode, fs2.Stream[F, Byte]]
required: Either[sttp.model.StatusCode, this.Out] which expands to Either[sttp.model.StatusCode, fs2.Stream[F, Byte]]

Using 0.18.0-M7 on Scala 2.13

Any ideas?

2 replies
Oliver Wickham

Hi, firstly thanks a lot for Tapir. It is a great project! I wonder if anyone can offer some advice for a situation I find myself in. I have some unusual requirements and am having trouble translating to Tapir. The JSON bodies received on some endpoints must include a field with a constant value. This is because the body is cryptographically signed and the intent must be clear to stop it being repurposed on other endpoints. As the field can only have one value, it doesn't really map so well onto a two way conversion (e.g. adding to Circle decoder). I attempted to add the field manually to some schemas using the following code (simplified version here)

def addTypeFieldToSchema[T](schema: Schema[T]) = {
    schema.schemaType match {
      case SProduct(info, fields) =>
        schema.copy(schemaType =
            fields.toList :+ (FieldName("type") -> Schema.schemaForString
                Validator.`enum`(List("value-to-appear-as-enum"), Some(_))

I was expecting that the OpenAPI would render the new type field as a string with an enum property with a single value. It does render the field, but the enum seems to have been lost. My questions are firstly: Is the approach I took the best for this constant field requirement. Secondly, any idea why the enum is lost when rendering OpenAPI? I have tried on version 0.17.8, then bumped to 0.17.9 and neither work. BTW, there were quite a few breaking changes from 0.17.8->0.17.9. Are there plans for SemVer in the future?

10 replies