Where communities thrive

  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
Repo info
  • 18:57
    mbore assigned #1357
  • 18:14
    adamw commented #1399
  • 18:13
    adamw opened #1399
  • 18:13

    adamw on infer-validator-encode-to-raw

    #1398: infer the encode-to-raw … (compare)

  • 14:01

    mergify[bot] on master

    Update client3:akka-http-backen… Merge pull request #1397 from s… (compare)

  • 14:01
    mergify[bot] closed #1397
  • 13:40
    kamilkloch opened #1398
  • 13:16
    mergify[bot] labeled #1397
  • 13:15
    scala-steward opened #1397
  • 08:52
    geirolz commented #1395
  • 08:52
    geirolz closed #1395
  • 08:52
    geirolz commented #1395
  • 07:50
    geirolz closed #1383
  • 07:49
    geirolz commented #1383
  • Jul 27 19:54

    mergify[bot] on master

    Update zio, zio-streams, zio-te… Merge pull request #1396 from s… (compare)

  • Jul 27 19:54
    mergify[bot] closed #1396
  • Jul 27 19:09
    mergify[bot] labeled #1396
  • Jul 27 19:08
    scala-steward opened #1396
  • Jul 27 15:59
    geirolz edited #1395
  • Jul 27 15:57
    geirolz opened #1395
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
Hi @adamw Do you plan some work regarding to processing multipart files in stream matter?
My case is next, I tring to process big csv file in reactive-like manner and now I oparsing multipart body manually. So it would be great to add such possibility. What do you think about this?
1 reply

Hello, I'm trying to define a custom schema mostly by hand, but the generated OpenAPI doesn't show the enum values for my field:

implicit val myResponseSchema: Schema[MyResponse] = Schema(
      FieldName("someEnum") -> schemaForEnumEntry[SomeEnum]
        .description("A description of the enum")
      FieldName("someNumber") -> Schema.schemaForDouble
  description = Some("Custom description of the response")

I'm using the tapir-enumeratum integration. If I stick to auto derivation, it works. If I compare both auto and my own schema objects, they look similar (save for the description and example). I'm a bit puzzled.

Impressive work with tapir
Was wondering if Schema works well with polymorphic and higher kinded case classes?
1 reply
Mateusz Wójcik

Hi guys, I'm playing with custom schema, I wanted to add additional validations to some nested NonEmptyList (e.g. max size):

case class Wrapper(items: NonEmptyList[String])

    implicit val customWrapperSchema: Schema[Wrapper] = implicitly[Derived[Schema[Wrapper]]]
      .modify(_.items)(_.validate(Validator.maxSize[String, List](1).contramap(_.toList)).description("Some description"))

    val yaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint.out(jsonBody[Wrapper]), Info("Fruits", "1.0")).toYaml

but this max size gets ignored:

      - items
      type: object
          type: array
            type: string
          description: Some description
          minItems: 1

am I doing something wrong or is it some kind of bug?

3 replies
Roman Makurin
hello guys, i have a strange question, but is possible on tapir level to modify response(put headers for example) - something like .in(extractFromRequest()) ?
Roman Makurin

i have code something like this

    .in("api" / "v1" / "do")
    .serverLogicForCurrentRecoverErrors((authBridge.auth _).tupled.andThen(handleError[F, AuthInfo[F]]))
    .serverLogicRecoverErrors(service.doWork.andThen(handleError[F, Json]))

but now i need to add headers to response back, what is the prefered way of doing this? I understand how to extract all needed info from request, but how to put back any calculated header?

Im in stuck now :)

1 reply
Mateusz Wójcik
Is there any way to add a section in OpenAPI for 400 Bad Request error which is by default returned for validation errors? My endpoint by itself does not have any error output, and I'd like to document in OpenAPI that 400 is returned when this and that validation on schema fails.
12 replies
Gabriel Asman

Hello, I've been working a Tapir API solution for my API and I find myself having a small issue. I had to define a custom schema for a few of my classes where auto-generation wouldn't suffice. But then I noticed that the validations my class's fields (in my case 1 field, an enum) was not showing up in the documentation. After going in deep with the debugger I found the place where that information is "lost"

On this line, rather the recursive call to apply being passed fieldSchema.validator as I might've expected, it instead uses the fieldValidatormethod to construct a new validator from the "parent"'s (current object's) validator. My first question is why is this so? My next question is what do I need to change in the parent validator in order to get my validator where I want it to. Looking at the logic of fieldValidator it looks like an Enum validator like the one I did would get discarded during the collect{ ... } pattern match.

                fieldName.encodedName -> apply(TypeData(fieldSchema, fieldValidator(typeData.validator, fieldName.name)))


Let me know if this makes sense and thanks in advance for your time!

Ooh, just scrolled up a bit and might be the same issue as @matwojcik 's softwaremill/tapir#1047
Thijs Broersen
Is there any way I can capture paths (arbitrary dept) until a certain path/pattern matches?
Thijs Broersen
e.g. endpoint.in(paths).in("fixed-path-key") client request builds a correct path from an arbitrary list of strings and ends with 'fixed-path-key'. But the backend endpoint never matches the request.
10 replies

Hello, is it possible to upload (stream) files of any size with arbitrary content type? The example on the website (https://tapir.softwaremill.com/en/latest/endpoint/streaming.html) doesn't work for me:

endpoint.out(streamBody(AkkaStreams)(Schema(Schema.derived[List[Person]].schemaType), CodecFormat.Json()))

In play2, there is the HttpEntity.Chunked type:

      def contentType = file.contentType.getOrElse("application/octet-stream")
      def chunks = GridFSStreams(gfs).source(file).map(HttpChunk.Chunk(_))

        header = ResponseHeader(OK),
        body = HttpEntity.Chunked(chunks, Some(contentType))

But I can not replicate the functionality on tapir side since streamBody requires a fixed codec format. Can I somehow avoid specifying the codec format or maybe at least specify a list of codec types?

13 replies