Where communities thrive

  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
Repo info
  • Sep 22 09:32
    geirolz opened #1498
  • Sep 21 16:48

    mergify[bot] on master

    Update http4s-blaze-client, ...… Merge pull request #1497 from s… (compare)

  • Sep 21 16:48
    mergify[bot] closed #1497
  • Sep 21 15:48
    mergify[bot] labeled #1497
  • Sep 21 15:47
    scala-steward opened #1497
  • Sep 21 13:43
    maxcom opened #1496
  • Sep 21 01:23
    mergify[bot] labeled #1495
  • Sep 21 01:23
    scala-steward opened #1495
  • Sep 20 10:27

    adamw on master

    Update cats-effect to 3.2.9 Merge pull request #1494 from s… (compare)

  • Sep 20 10:27
    adamw closed #1494
  • Sep 18 07:29

    mergify[bot] on master

    Update scalacheck-1-15 to 3.2.1… Merge branch 'master' into upda… Update scalacheck-1-15 to 3.2.1… and 1 more (compare)

  • Sep 18 07:29
    mergify[bot] closed #1493
  • Sep 18 06:29
    mergify[bot] labeled #1494
  • Sep 18 06:29
    scala-steward opened #1494
  • Sep 18 06:29
    scala-steward synchronize #1493
  • Sep 18 02:14

    mergify[bot] on master

    Update scalatest to 3.2.10 Merge pull request #1492 from s… (compare)

  • Sep 18 02:14
    mergify[bot] closed #1492
  • Sep 18 01:25
    mergify[bot] labeled #1493
  • Sep 18 01:25
    mergify[bot] labeled #1492
  • Sep 18 01:25
    scala-steward opened #1493
Daniel Robert
possibly I'm confused because Tapir seems to require me to handle the error but the http4s service it'll be generating has some error handling built in
Daniel Robert

perhaps more specifically: documentation in Exception Handling states:

If the logic function, which is passed to the server interpreter, fails (i.e. throws an exception, which results in a failed Future or IO/Task), this is propagated to the library (akka-http or http4s).

But I fail to see how it gets propagated; seems like the errorOut declaration (to coerce the error type to Throwable) would result in this being caught/handled by tapir. At the last, it shows up in OpenAPI documentation with whatever status code mapping is applied to Throwable

6 replies
Hi @adamw, if you can update the sttp deps (sttp-client, sttp-model, sttp-shared) to the latest versions in https://github.com/softwaremill/tapir/blob/master/project/Versions.scala, that'd be the great. In the meantime, I'm updating a couple of breaking changes in tapir 0.17.1 (namely toRoutes and toOpenAPI). Looking forward to more tapir/sttp awesomeness :)
5 replies
Daniel Robert
I have a case class that I also have circe codecs defined for. I've added @description("blah") to the case class. Using automatic derivation, I do not see this description in the resulting doc. But using implicitly[Derived[Schema[MyType]].value.description("but this works") I do see such a thing.
Is this expected behavior?
6 replies
Daniel Robert
seems to work as expected with semi-auto derivation (but not fully auto derivation)
Adam Warski

Thank you for being part of the tapir community - Merry Christmas & a more regular 2021 ;)

who knows, maybe we'll manage to bring tapir to 1.0 in the next year? :)


Hi, I'm digging around AsyncAPI docs generation, got success with some initial stuff,
but not sure how to customize documentation of in/out messages.

For example:

case class Response(hello: String)

val wsEndpoint: Endpoint[Unit, Unit, Flow[String, Response, Any], AkkaStreams with WebSockets] =
  endpoint.get.in("ping").out(webSocketBody[String, CodecFormat.TextPlain, Response, CodecFormat.Json](AkkaStreams))

How to specify the 'description' tag of 'Response' here?

2 replies
Is there way to use ziostream endopint with http4s server?
Examples show only Akkastrems with akka-http or http4s + fs2 streaming.
And it looks like http4s has no capabilities with ziostreams.
if so what is the best practice of using Ziostream as endpoint with tapir?
Jens Grassel
FYI: I updated the gitter8 template for the http4s-tapir service to work with 0.17. Just create it as usual via sbt new https://codeberg.org/wegtam/http4s-tapir.g8.git and you're ready to go.
Daniel Robert
I'm finding an awkward data model in my code base that's effectively Array[Tuple2[TypeA, TypeB]] and I can't find any auto- or semiauto-derived way of generating a valid schema.
On the Json side, this is ultmately [ [ { /* type a */ }, { /* type b */ } ], [ ... ] ]. I'm not entirely sure if this is supported by openapi but if so, could tapir generate such a data type?
4 replies
Anders Schwartz

Hi! I am using tapir to create a post endpoint (with http4s as backend) that can receive a file (as byte array) through multipart submission and I am looking to add a filter/check that would stop submitters from uploading files over a certain size, e.g. 100MB (this is mainly to protect the server from malicious use). What would be the idiomatic way to implement such a check using tapir? I am looking to fail the upload if the uploaded content exceeds the limit (and not wait until the full upload has completed).

I've considered a couple of options but none seem great:

  • check the content-length header (but it could be missing or inaccurate and I only seem to be able to evaluate it after the data has been received by http4s)
  • check the File size after uploaded (again too late)
  • somehow process the stream as it's coming in and reject immediately if the accumulated upload exceeds. I haven't been able to track down any resources/docs/examples on this option.
  • set max chunk size (but that would still not prohibit the full upload to exceed the limit)
  • using a stream as an input and somehow keeping a tally of the accumulated input and failing fast
1 reply
Daniel Robert
Codec.mapDecode - if I'm reading this correctly, it will always reset the validator to Validator.pass.
It delegates to map(Mapping.fromDecode(...)) which:
  • Mapping.fromDecode hard-codes Validator.pass
  • map(codec: Mapping[H, HH]]) calls outer.schema.contramap(codec.encode).validate(codec.validator) which will replace the Schemas validator with the new always pass version, won't it?
Daniel Robert
is it intentional that emptyOutput does not allow for a .description?
Evgenii Kuznetcov

Hello folks!
First, thank you for the awesome library!

I'm trying learn enough of it to protect myself against Spring Boot at work.

Spring allows to generate typed http clients in a single step:
Something like (Api, Url) => ClientApiImpl. This is what people usually want: just an object with (I => Future[O]) method and all http stuff being encapsulated.

As far as I could find, Tapir requires to do some plumbing to achieve that.
1) create an http interpreter val cl = SttpClientInterpreter.toRequestUnsafe(apiDef, url)
2) create a backend val backend = HttpURLConnectionBackend()
3) Manually declare an http-free interface, write boilerplate implementation like i => backend.send(cl(i))

Is there an interpreter that takes an api, url, backend (optional?) and does it for me? That would be much more easy to sell.

Evgenii Kuznetcov
Just understood that it's achievable with a helper function:
 def mkSimpleClient[I, E, O](api: ZEndpoint[I, E, O], uri: String): (I => Either[E, O]) = {
    val backend = HttpURLConnectionBackend()
    val fn = SttpClientInterpreter
      .toRequestUnsafe(api, Uri(uri))

    i: I => {
Evgenii Kuznetcov
I made a PR about this topic: softwaremill/tapir#920

@adamw , just an FYI, I think currently sttp-client 3.0.0-RC14 doesn't work with tapir 0.17.1, because sttp-client 3.0.0-RC14 uses sttp-model 1.2.0-RC12, which has changes that are not binary compatible with the version that tapir 0.17.1 uses. I get module-not-found error when using them together. No big deal as I'm sure you're aware of that and you're in the middle of preparing a Tapir release :)

Another thing I noticed is that sttp-shared 1.0.0-RC12 is still using sttp-model 1.2.0-RC11. With the aforementioned compatibility issue, it'd like to sync all of them :)

I've kind of figured out the dependency relationship of these modules:
tapir <depends on> sttp-client <depends on> sttp-shared <depends on> sttp-model
haha. Thanks.

4 replies

Hi Adam and all, my question is in regards to setting headers in HTTP response:

If I want to return headers such as X-Frame-Options and X-XSS-Protection with static/fixed values, what is the best way to add them in Tapir endpoints?

The standard approach is to build off the base Endpoint, add .out( header[A]("foo") ) then, before returning in .serverLogic, I add the values to complete the tuple. For example, if my server logic returns a Long and the values of both my http headers are strings, my tuple would be (Long, String, String).

With this approach, I have to repeat this for all my endpoints.

I can use either serverLogicForCurrent or serverLogicPart. With serverLogicForCurrent, I'd add/resolve the header values in my base endpoints. However, that will pass down these values down to serverLogic and I will have to compose the final tuple at the end of serverLogic.

I haven't used serverLogicPart. From the docs, it seems that, once the values are added into the headers, the endpoint is "considered complete, and cannot be later extended", as I'm quoting from the docs. This isn't what I'm looking for. What I'm looking for is a hybrid of serverLogicForCurrent and serverLogicPart: I'd like to resolve the header values ahead of serverLogic and then allow this base implementation be to further composed without the need of passing down these header values (because they are already resolved) as well as the re-assembly at the end.

I might be overthinking this or I might misunderstand the docs. Please correct me if that's the case.

All these sound a bit complicated, haha. That's kind of my point. Basically I'm looking for an elegant/idiomatic way in Tapir that allows me to build a base endpoint with fixed headers (with static values). Other endpoints can then be derived from this base implementation and all these endpoints will return the same headers in response.

Any suggestions are welcome. Thank you.

6 replies
Denis Novac
Hi everyone. I guess, there is an error in migration guide to 0.17 (on release page). It says "instead stream types, e.g. Endpoint[I, E, O, Source[ByteString, Any]], use the capability: Endpoint[I, E, O, Source[ByteString, Any]]" but it is the same code written twice. What should i do to migrate? It seems that Endpoint[I, E, O, Fs2Streams[F]] does not work.
Denis Novac
The Endpoint[I, E, O, Fs2Streams[F]] change actually works
1 reply
Denis Novac

Is it possible to create schema for Map type?

This code says there is no implicit Schema:

val endpoint
      : Endpoint[Unit, (StatusCode, EndpointError), Map[MyIdClass, List[Description]], Any] =
      .out(jsonBody[Map[MyIdClass, List[Description]]])

Every custom type has Schema in companion. For example, this type works good:

List[(MyIdClass, List[Description])]
1 reply
Vladimir Bodnartchouk
Hello, i would like to use tapir with https://github.com/RustedBones/akka-http-metrics
is there a custom way to specify the path ? In my case i would like to declare an Endpoint but use pathLabeled directive to let akka-http-metrics create prometheus metrics.
1 reply
Denis Novac

Hi everyone. Is it possible to get schemaType for some T from ADT with schema? Something like this kind function perhaps (this one does not work)?


import sttp.tapir.{Schema, SchemaType}
import sttp.tapir.generic.{Derived, SchemaDerivation}

sealed trait Animal extends SchemaDerivation

object Animal extends SchemaDerivation {

  implicit val schema = Schema.derived[Animal]

  final case class Cat(name: String) extends Animal
  final case class Dog(name: String) extends Animal
  final case class Horse(name: String, speed: Float) extends Animal


def kind[T <: Animal](t: T): SchemaType = Schema.derived[T].schemaType
3 replies
Denis Novac

Hi everyone. Is it possible to get schemaType for some T from ADT with schema? Something like this kind function perhaps (this one does not work)?

Also it seems that Schema.derived is enough. I though it takes some implicits from SchemaDerivation. What is the difference between Schema.derived and SchemaDerivation mixing?

5 replies

Hi, my errors code looks like this.

sealed trait ApiError { self =>

  val code = self match {
    case ApiError.UnauthorizedError(_) => 401
    case ApiError.NotFoundError(_) => 404
    case ApiError.BadRequestError(_) => 400


object ApiError {

  case class UnauthorizedError(reason: String) extends ApiError

  case class NotFoundError(message: String) extends ApiError

  case class BadRequestError(reason: String) extends ApiError


I'm getting this result

HTTP/1.1 400 BadRequest
Content-Length: 14
Content-Type: application/json
Date: Thu, 14 Jan 2021 18:03:45 GMT
Server: akka-http/10.2.2

    "reason": ""

Is there a way to properly serialize inherited parameters? Do I have to create a custom decoder and encoder?

31 replies
Denis Novac
Hi everyone. Getting this error while migrating to 0.17:
Cause: java.lang.IllegalArgumentException: Validator reference is already set to Product(Map(filter -> sttp.tapir.generic.internal.SchemaMagnoliaDerivation$$anon$1@7442d39b))!
at sttp.tapir.Validator$Ref.set(Validator.scala:228)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$2(SchemaMagnoliaDerivation.scala:33)
at scala.Option.foreach(Option.scala:407)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$1(SchemaMagnoliaDerivation.scala:33)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.withProgressCache(SchemaMagnoliaDerivation.scala:76)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine(SchemaMagnoliaDerivation.scala:16)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine$(SchemaMagnoliaDerivation.scala:15)
at sttp.tapir.Schema$.combine(Schema.scala:99)
I guess somewhere i have two schemas for my type or something?
2 replies
I just write val x = MyType(...) and this happens in runtime. No compile errors
Denis Novac
Actually MyType does not even have a schema, it is only for internal usage.
Denis Novac

Hi everyone. I just noticed that all my docs routes have index.html page with Swagger Petstore (default Swagger example). E.g.:

My OpenAPI documentation: localhost:8080/api/v1/docs
Swagger Petstore: localhost:8080/api/v1/docs/index.html

And so on every controller.

How do you disable it?

1 reply
Morning all, just trying to use tapir for the first time (with http4s) and I've come a bit unstuck handling errors. In my current code base, I have a function along the lines of def handleResponse[Resp: Encoder](resp: Either[MyError, Resp]): IO[Response[IO]] = ... which I use to handle different instances of MyError (it's a trait with some case classes) and transform them into Responses. I can't figure out how to achieve this in tapir. I've had a look at the documentation, but neither errorOut nor the approach to just recover from the errors quite fits. Any advice very much appreciated
2 replies
(The problem appears to be that Http4sServerInterpreter.toRoutes handles the mapping from Either[MyError, Resp] to IO[Response[IO]] automatically)
Josip Grgurica
8 replies
Matthew Tovbin
Hi folks. Is there a way to attach documentation to request/response types for the API, e.g. case class Book(/*This is a title of the book*/ title: String)?
6 replies
Gopal Shah

Hi all, I am looking to have an api with query parameters having defaults values, similar to https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/parameter-directives/parameters.html


Does Tapir support this?

5 replies
Thijs Broersen
I have an issue with using and endpoint as a client. The endpoint returns a 307 http status code (server encoding works fine) but I get a decoding failure. When I remove the 307 status, this line .out(statusCode(StatusCode.TemporaryRedirect)), the client can correctly decode. Should I do this differently? Is this a bug?
Thijs Broersen
I suspect the client is following the redirect ..
Thijs Broersen
Tried set false to any redirect, does not help. I only see exhausted input as decode result error. Doing a manual request on the API I do get what I expect.
Thijs Broersen
building the request by hand (disabling redirects) does work basicRequest.followRedirects(false)
2 replies
Srepfler Srdan
where are we with the asyncapi work? I think I've found an interesting use case in Lagom, for example they have some servicec endpoints which are stream on websocket but they also generate a kafka stream for the events that are generated. It would be really cool if one Tapir definition would generate both the Play endpoints (stream and non stream) but also the kafka events. In the Lagom API also there's this compositional step Command -> Event which is also interesting
2 replies
Assen Kolov
How can I access the hashtag value in a request like GET /cognito/login#token=123&type=Bearer ? I don't have much control over the request, I want to handle it as it is. The hashtag value seems to be lost in the ServerRequest instance.
Assen Kolov
Aha: fragment (#) is not sent to the server at all, must be handled in browser :-)
Pouria Mellati
Hi all, is there a way to specify a namespaced name for schema objects? For instance, if endpoint one returns my.scala.packageone.User and endpoint two returns my.scala.packagetwo.User, how can I ensure that the two versions of User are represented with correct package / namespace names?
3 replies
Pascal Danek

Hello guys - bravo for this extraordinary project it has changed my life in coding Rest APIs in Scala :) I come here because I have a problem with having Scala built in enumerations schema in my openapi doc.

Here is a code snippet that reproduces the issue (mainly coming fro the documentation):

import sttp.tapir._
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody
import io.circe.generic.auto._
import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter
import sttp.tapir.openapi.circe.yaml._

trait EnumHelper { e: Enumeration =>
  import io.circe._

  implicit val enumDecoder: Decoder[e.Value] = Decoder.decodeEnumeration(e)
  implicit val enumEncoder: Encoder[e.Value] = Encoder.encodeEnumeration(e)

  implicit val schemaForEnum: Schema[e.Value]       = Schema.string
  implicit def validatorForEnum: Validator[e.Value] = Validator.`enum`(e.values.toList, v => Option(v))

object Color extends Enumeration with EnumHelper {
  type Color = Value
  val Blue = Value("blue")
  val Red  = Value("red")

case class Drawing(color: Color.Value)

val ep = endpoint.in(jsonBody[Drawing])

Now I generate the openapi documentation with: println(OpenAPIDocsInterpreter.toOpenAPI(ep, "My Project", "1.0").toYaml)

And here is what I get:

openapi: 3.0.3
  title: My Project
  version: '1.0'
      operationId: getRoot
              $ref: '#/components/schemas/Drawing'
        required: true
          description: ''
      - color
      type: object
          type: string

So as you can see, no enumeration values.

Do you have an idea about what's going on here ?

Thanks in advance for your help.

7 replies
Pascal Danek

Also I have a second problem with the use of serverLogic on my endpoints. The compiler complains about a type issue, but I guess it is a problem of inference maybe you can help me there. Here is how to reproduce:

import io.circe.generic.auto._
import monix.eval.Task
import sttp.tapir._
import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody

case class Foo(value: String)

val ep1 = endpoint.out(stringBody).in(jsonBody[Foo]).serverLogic[Task] { _ =>

val ep2 = endpoint.out(stringBody).in("foo")

val eps = Seq(ep1, ep2)

println(OpenAPIDocsInterpreter.toOpenAPI(eps, "My Project", "1.0").toYaml)
overloaded method toOpenAPI with alternatives:
(es: Iterable[sttp.tapir.Endpoint[_, _, _, _]],title: String,version: String)(implicit options: sttp.tapir.docs.openapi.OpenAPIDocsOptions): sttp.tapir.openapi.OpenAPI <and>
[I, E, O, S, F[_]](se: sttp.tapir.server.ServerEndpoint[I,E,O,S,F], title: String, version: String)(implicit options: sttp.tapir.docs.openapi.OpenAPIDocsOptions): sttp.tapir.openapi.OpenAPI <and>
[I, E, O, S](e: sttp.tapir.Endpoint[I,E,O,S], title: String, version: String)(implicit options: sttp.tapir.docs.openapi.OpenAPIDocsOptions): sttp.tapir.openapi.OpenAPI
cannot be applied to (Seq[Product with sttp.tapir.EndpointInfoOps[_ >: Unit with Foo, Unit, String, Any] with sttp.tapir.EndpointMetaOps[_ >: Unit with Foo, Unit, String, Any] with java.io.Serializable], String, String)
println(OpenAPIDocsInterpreter.toOpenAPI(eps, "My Project", "1.0").toYaml)

Note that as soon as I am starting to use serverLogic I cannot concatenate anymore my endpoints. The compiler complains:

type mismatch;
 found   : Seq[sttp.tapir.Endpoint[_ >: Unit with String, (ilogyou.web.endpoints.EndpointError, sttp.model.StatusCode), _ >: Unit with ilogyou.modules.LoginResponse, Any]]
 required: scala.collection.IterableOnce[Product with sttp.tapir.EndpointInfoOps[...

Thanks again for your help.

3 replies
Oleksandr Shtykhno

hi, I need to use

Endpoint[SomeCaseClass, SomeErrorInfo, ZioStreams.BinaryStream, ZioStreams]

but it looks like integration for ZIO streams and Http4s is not implemented yet, I would appreciate any tip to the right direction

4 replies
Pascal Mengelt

Hi, I use Tapir to generate Open API. I could not figure out how to set a default value for a path variable.
This is my path:

.in(("process-definition" / "key" / path[String]("processDefinitionKey") / "tenant-id" / path[String]("tenant-id") / "execute"))

The output should be like:

      - name: tenant-id
        in: path
        required: true
          type: string
          default: myTenant
4 replies

Hi there! I have such a situation (input data is in url-encoded format):

case class Foo(foo: Option[String])
case class Bar(bar: Option[Foo])

implicit def fooCodec: Codec[Option[String], Foo, CodecFormat.TextPlain] =
  Codec.option[String, String, CodecFormat.TextPlain]

def func: Endpoint[Bar, Unit, String, Any] = endpoint

And compiler says to me the next:

exception during macro expansion:
scala.reflect.macros.TypecheckException: Cannot find a codec between types: List[String] and Option[Foo], formatted as: sttp.tapir.CodecFormat.TextPlain.
Did you define a codec for: Option[Foo]?
Did you import the codecs for: sttp.tapir.CodecFormat.TextPlain?

What did I do wrong?

2 replies

bauerflo @bauerflo 11:34
Hi! I believe there might be something wrong with the tapir backend stub tests
my endpoint looks like this

val destinationEndpoint = endpoint

val postDestination = destinationEndpoint.post
.in("companies" / path[TenantId] / "destinations")

and my stubs:

.apply[Future, Capabilities]
.whenInputMatches(Endpoints.postDestination)(_._4 == destination)

my destination definitely goes in here (in debugging)

.apply((token, traceId, tenantId, destination))

the input in the stub however takes all parameters correctly but the destination is null (so, the stub works if I match on _._4 == null)

I noticed in debugging that req below contains all input parameters and the last one is a StringBody(...) containing my (correctly serialized) destination object as json

def whenInputMatchesI, E, O(inputMatcher: I => Boolean): TypeAwareWhenRequest[I, E, O] = {
new TypeAwareWhenRequest(
new stub.WhenRequest(req =>
DecodeInputs(endpoint.input, new SttpDecodeInputs(req)) match {
case _: DecodeInputsResult.Failure => false

however it checks out in DecodeInputsResult.Failure

4 replies
Pascal Danek
Hello guys, I am desperately trying to build a ServerEndpoint returning a fs2 stream of json objects on http4s. Can someone point me to some examples based on the latest tapir version ? The string based doc examples does not allow me to understand how I am supposed to proceed. There are also conflicting examples on the internet giving different streamBody definitions. Lets say I have a case class Person(name: String), a service def getPersons: fs2.Stream[Task, Person]. What is the corresponding .out(streamBody(???)) and how in the serverLogic i can transform this type into something accepted by Tapir on http4s ? Thanks for any help !
5 replies