-
-
Notifications
You must be signed in to change notification settings - Fork 249
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide more examples of tapir interop (#2375)
- Loading branch information
1 parent
193714f
commit 8df7696
Showing
8 changed files
with
191 additions
and
18 deletions.
There are no files selected for viewing
This file contains 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
62 changes: 62 additions & 0 deletions
62
examples/src/main/scala/example/calibantotapir/MainApp.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package example.calibantotapir | ||
|
||
import example.calibantotapir.graphql._ | ||
import zio._ | ||
import zio.http._ | ||
import caliban._ | ||
import caliban.interop.tapir._ | ||
import sttp.tapir.json.jsoniter._ | ||
import sttp.capabilities.zio.ZioStreams | ||
import sttp.tapir.server.ziohttp._ | ||
import sttp.tapir.server.ServerEndpoint | ||
import sttp.tapir.swagger.bundle.SwaggerInterpreter | ||
import example.calibantotapir.tapir.SampleRestEndpoint | ||
|
||
object MainApp extends ZIOAppDefault { | ||
val graphQLToTapir: ZIO[Queries & Mutations, CalibanError, List[ServerEndpoint[ZioStreams, Task]]] = | ||
for { | ||
queries <- ZIO.service[Queries] | ||
mutations <- ZIO.service[Mutations] | ||
graphql = graphQL(RootResolver(queries, mutations)) | ||
interpreter <- graphql.interpreter | ||
endpoints = | ||
HttpInterpreter(interpreter) | ||
.serverEndpoints[Any, ZioStreams](ZioStreams) | ||
.map(_.prependSecurityIn("graphql")) | ||
} yield endpoints | ||
|
||
def documented(allEndpoints: List[ServerEndpoint[ZioStreams, Task]]) = { | ||
val doc = SwaggerInterpreter().fromServerEndpoints(allEndpoints, "playground", "1.0") | ||
doc ++ allEndpoints | ||
} | ||
|
||
// Redirect / to /docs | ||
val redirectRootToDocs = | ||
Routes( | ||
Method.GET / "" -> | ||
handler(Response.redirect(URL(path = Path.root / "docs"), isPermanent = true)) | ||
) | ||
|
||
val serve: ZIO[Queries & Mutations & Server, CalibanError, Nothing] = | ||
for { | ||
graphqlEndpoints <- graphQLToTapir | ||
restEndpoint = SampleRestEndpoint.endpoint | ||
all = documented(restEndpoint :: graphqlEndpoints) | ||
routes = ZioHttpInterpreter().toHttp[Any](all) | ||
server <- ZIO.service[Server] | ||
_ <- Server.install(redirectRootToDocs ++ routes) | ||
port <- server.port | ||
_ <- ZIO.logInfo(s"Server started on port $port") | ||
result <- ZIO.never | ||
} yield result | ||
|
||
override val run = | ||
serve | ||
.provide( | ||
BookRepository.layer, | ||
Queries.layer, | ||
Mutations.layer, | ||
Server.live, | ||
ZLayer.succeed(Server.Config.default) | ||
) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Caliban to Tapir | ||
|
||
This example shows how to convert a Caliban GraphQL datatypes and turn them into Tapir endpoints. | ||
This is useful when you have both REST APIs and GraphQL endpoints and you want to use Tapir to expose a unified API. | ||
|
||
The example also shows how to mount the GraphQL endpoint to `POST` and `GET` `/graphql` as well as provide `/example` for a sample endpoint returning JSON. | ||
All of the endpoints are documented in Swagger. Visiting `localhost:8080/` or `/docs` will show the Swagger UI. | ||
|
||
This example interprets the Tapir endpoints using ZIO HTTP. | ||
|
||
Note: If you are only using GraphQL endpoints, then you are much better of using the quick adapter instead of this. This example is provided for when you want to use both GraphQL and REST endpoints and have them managed by Tapir to gain the benefits of documentation. |
69 changes: 69 additions & 0 deletions
69
examples/src/main/scala/example/calibantotapir/graphql/Book.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package example.calibantotapir.graphql | ||
|
||
import caliban.schema.ArgBuilder | ||
import caliban.schema.ArgBuilder.auto._ | ||
import caliban.schema.Schema | ||
import caliban.schema.Schema.auto._ | ||
import zio._ | ||
|
||
final case class Book(title: String, year: Int) | ||
object Book { | ||
implicit val bookSchema: Schema[Any, Book] = Schema.gen | ||
} | ||
|
||
final case class BookSearchArgs(year: Option[Int], limit: Option[Int]) | ||
object BookSearchArgs { | ||
implicit val bookSearchArgsSchema: ArgBuilder[BookSearchArgs] = ArgBuilder.gen | ||
} | ||
|
||
class BookRepository(books: Ref[List[Book]]) { | ||
def add(book: Book): UIO[Unit] = books.update(books => book :: books) | ||
|
||
def delete(title: String): UIO[Unit] = books.update(_.filterNot(_.title == title)) | ||
|
||
def list(year: Option[Int], limit: Option[Int]): UIO[List[Book]] = { | ||
val lim = limit.getOrElse(Int.MaxValue) | ||
val yearMatch = (incomingYear: Int) => year.map(_ == incomingYear).getOrElse(true) | ||
books.get.map(_.filter(book => yearMatch(book.year)).take(lim)) | ||
} | ||
} | ||
object BookRepository { | ||
val books = List( | ||
Book("The Sorrows of Young Werther", 1774), | ||
Book("Iliad", -8000), | ||
Book("Nad Niemnem", 1888), | ||
Book("The Colour of Magic", 1983), | ||
Book("The Art of Computer Programming", 1968), | ||
Book("Pharaoh", 1897), | ||
Book("Lords and Ladies", 1992) | ||
) | ||
|
||
val layer: ULayer[BookRepository] = ZLayer(Ref.make[List[Book]](books).map(new BookRepository(_))) | ||
} | ||
|
||
final case class Queries( | ||
books: BookSearchArgs => UIO[List[Book]] | ||
) | ||
object Queries { | ||
implicit val queriesSchema: Schema[Any, Queries] = Schema.gen | ||
|
||
val layer: URLayer[BookRepository, Queries] = ZLayer { | ||
for { | ||
repo <- ZIO.service[BookRepository] | ||
} yield Queries(args => repo.list(args.year, args.limit)) | ||
} | ||
} | ||
|
||
final case class Mutations( | ||
addBook: Book => UIO[Unit], | ||
deleteBook: String => UIO[Unit] | ||
) | ||
object Mutations { | ||
implicit val mutationsSchema: Schema[Any, Mutations] = Schema.gen | ||
|
||
val layer: URLayer[BookRepository, Mutations] = ZLayer { | ||
for { | ||
repo <- ZIO.service[BookRepository] | ||
} yield Mutations(repo.add, repo.delete) | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
examples/src/main/scala/example/calibantotapir/tapir/SampleRestEndpoint.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package example.calibantotapir.tapir | ||
|
||
import sttp.tapir.ztapir._ | ||
import sttp.tapir.json.jsoniter._ | ||
import zio._ | ||
import sttp.capabilities.zio.ZioStreams | ||
import io.circe.JsonObject | ||
import zio.constraintless.Sample | ||
import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec | ||
import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker | ||
import sttp.tapir.Schema | ||
|
||
object SampleRestEndpoint { | ||
// NOTE: You can also add the graphiql endpoint in a similar fashion | ||
val endpoint: ZServerEndpoint[Any, ZioStreams] = | ||
infallibleEndpoint.get | ||
.in("example") | ||
.out(jsonBody[SampleResponse]) | ||
.serverLogic(_ => ZIO.right(SampleResponse("Hello", 1, 2))) | ||
|
||
final case class SampleResponse(data: String, x: Int, y: Int) | ||
object SampleResponse { | ||
implicit val sampleResponseCodec: JsonValueCodec[SampleResponse] = JsonCodecMaker.make | ||
implicit val sampleResponseSchema: Schema[SampleResponse] = Schema.derived | ||
} | ||
} |
This file contains 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
4 changes: 2 additions & 2 deletions
4
...main/scala/example/tapir/ExampleApp.scala → ...a/example/tapirtocaliban/ExampleApp.scala
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Tapir to Caliban | ||
|
||
This example shows how you can take Tapir endpoints and turn them into Caliban GraphQL datatypes. |