With tapir, you can describe HTTP API endpoints as immutable Scala values. Each endpoint can contain a number of input and output parameters. An endpoint specification can be interpreted as:
- a server, given the "business logic": a function, which computes output parameters based on input parameters. Currently supported:
- a client, which is a function from input parameters to output parameters. Currently supported:
- documentation. Currently supported:
Depending on how you prefer to explore the library, take a look at one of the examples or head over to the docs for a more detailed description of how tapir works! Or, use adopt-tapir to generate a tapir-based project in a couple of clicks!
- type-safety: compile-time guarantees, develop-time completions, read-time information
- declarative: separate the shape of the endpoint (the "what"), from the server logic (the "how")
- OpenAPI / Swagger integration: generate documentation from endpoint descriptions
- observability: leverage the metadata to report rich metrics and tracing information
- abstraction: re-use common endpoint definitions, as well as individual inputs/outputs
- library, not a framework: integrates with your stack
Is your company already using tapir? We're continually expanding the "adopters" section in the documentation; the more the merrier! It would be great to feature your company's logo, but in order to do that, we'll need written permission to avoid any legal misunderstandings.
Please email us at firstname.lastname@example.org from your company's email with a link to your logo (if we can use it, of course!) or with details who to kindly ask for permission to feature the logo in tapir's documentation. We'll handle the rest.
import sttp.tapir._ import sttp.tapir.generic.auto._ import sttp.tapir.json.circe._ import io.circe.generic.auto._ type Limit = Int type AuthToken = String case class BooksQuery(genre: String, year: Int) case class Book(title: String) // Define an endpoint val booksListing: PublicEndpoint[(BooksQuery, Limit, AuthToken), String, List[Book], Any] = endpoint .get .in(("books" / path[String]("genre") / path[Int]("year")).mapTo[BooksQuery]) .in(query[Limit]("limit").description("Maximum number of books to retrieve")) .in(header[AuthToken]("X-Auth-Token")) .errorOut(stringBody) .out(jsonBody[List[Book]]) // Generate OpenAPI documentation import sttp.apispec.openapi.circe.yaml._ import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter val docs = OpenAPIDocsInterpreter().toOpenAPI(booksListing, "My Bookshop", "1.0") println(docs.toYaml) // Convert to akka-http Route import sttp.tapir.server.akkahttp.AkkaHttpServerInterpreter import akka.http.scaladsl.server.Route import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global def bookListingLogic(bfy: BooksQuery, limit: Limit, at: AuthToken): Future[Either[String, List[Book]]] = Future.successful(Right(List(Book("The Sorrows of Young Werther")))) val booksListingRoute: Route = AkkaHttpServerInterpreter() .toRoute(booksListing.serverLogic((bookListingLogic _).tupled)) // Convert to sttp Request import sttp.tapir.client.sttp.SttpClientInterpreter import sttp.client3._ val booksListingRequest: Request[DecodeResult[Either[String, List[Book]]], Any] = SttpClientInterpreter() .toRequest(booksListing, Some(uri"http://localhost:8080")) .apply((BooksQuery("SF", 2016), 20, "xyz-abc-123"))
tapir documentation is available at tapir.softwaremill.com.
Quickstart with sbt
Add the following dependency:
"com.softwaremill.sttp.tapir" %% "tapir-core" % "1.5.1"
And finally, type
endpoint. and see where auto-complete gets you!
Partial unification is now enabled by default from Scala 2.13. However, if you're using Scala 2.12 or older, then you'll need partial unification enabled in the compiler (alternatively, you'll need to manually provide type arguments in some cases):
scalacOptions += "-Ypartial-unification"
Sidenote for scala 2.12.4 and higher: if you encounter an issue with compiling your project because of
StackOverflowException related to this scala bug,
please increase your stack memory. Example:
sbt -J-Xss4M clean compile
Other sttp projects
sttp is a family of Scala HTTP-related projects, and currently includes:
- sttp client: the Scala HTTP client you always wanted!
- sttp tapir: this project
- sttp model: simple HTTP model classes (used by client & tapir)
- sttp shared: shared web socket, FP abstractions, capabilities and streaming code.
- sttp apispec: OpenAPI, AsyncAPI and JSON Schema models.
All suggestions welcome :)
See the list of issues and pick one! Or report your own.
If you are having doubts on the why or how something works, don't hesitate to ask a question on
discourse or via github. This probably means that the documentation, scaladocs or
code is unclear and be improved for the benefit of all.
In order to develop the documentation, you can use the
doc/watch.sh script, which runs Sphinx using Python.
doc/requirements.txt to set up your Python environment with
pip. If you're using Nix, you can just run
doc directory to set up a working shell with all the dependencies.
core module needs to remain binary-compatible with earlier versions. To check if your changes meet this requirement,
you can run
core/mimaReportBinaryIssues from the sbt console.
The JS tests use Gecko instead of Chrome, although this causes another problem: out of memory when running JS tests for multiple modules. Work-arounds:
- run only tests for a specific Scala version and platform using
testScoped 2.13 JS(supported versions: 2.12, 2.13, 3; supported platforms: JVM, JS, Native)
- test single JS projects
- use CI (GitHub Actions) to test all projects - the
.github/workflows/ci.ymlenumerates them one by one
You can test only server/client/doc/other projects using
To verify that the code snippet in docs compile, run
compileDocumentation. A full mdoc run is done during a release
(when the documentation is generated).
Importing into IntelliJ
By default, when importing to IntelliJ, only the Scala 2.13/JVM subprojects will be imported. This is controlled by the
ideSkipProject setting in
If you'd like to work on a different platform or Scala version, simply change this setting temporarily so that the correct subprojects are imported. For example:
// import only Scala 2.13, JS projects ideSkipProject := (scalaVersion.value != scala2_13) || !thisProjectRef.value.project.contains("JS") // import only Scala 3, JVM projects ideSkipProject := (scalaVersion.value != scala3) || thisProjectRef.value.project.contains("JS") || thisProjectRef.value.project.contains("Native"), // import only Scala 2.13, Native projects ideSkipProject := (scalaVersion.value != scala2_13) || !thisProjectRef.value.project.contains("Native")
We offer commercial support for tapir and related technologies, as well as development services. Contact us to learn more about our offer!
Copyright (C) 2018-2023 SoftwareMill https://softwaremill.com.